Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/pgraph/transformState.cxx

Go to the documentation of this file.
00001 // Filename: transformState.cxx
00002 // Created by:  drose (25Feb02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "transformState.h"
00020 #include "compose_matrix.h"
00021 #include "bamReader.h"
00022 #include "bamWriter.h"
00023 #include "datagramIterator.h"
00024 #include "indent.h"
00025 #include "compareTo.h"
00026 
00027 TransformState::States *TransformState::_states = NULL;
00028 CPT(TransformState) TransformState::_identity_state;
00029 TypeHandle TransformState::_type_handle;
00030 TypeHandle EventStoreTransform::_type_handle;
00031 
00032 ////////////////////////////////////////////////////////////////////
00033 //     Function: TransformState::Constructor
00034 //       Access: Protected
00035 //  Description: Actually, this could be a private constructor, since
00036 //               no one inherits from TransformState, but gcc gives us a
00037 //               spurious warning if all constructors are private.
00038 ////////////////////////////////////////////////////////////////////
00039 TransformState::
00040 TransformState() {
00041   if (_states == (States *)NULL) {
00042     // Make sure the global _states map is allocated.  This only has
00043     // to be done once.  We could make this map static, but then we
00044     // run into problems if anyone creates a TransformState object at
00045     // static init time; it also seems to cause problems when the
00046     // Panda shared library is unloaded at application exit time.
00047     _states = new States;
00048   }
00049   _saved_entry = _states->end();
00050   _self_compose = (TransformState *)NULL;
00051   _flags = F_is_identity | F_singular_known;
00052   _inv_mat = (LMatrix4f *)NULL;
00053 }
00054 
00055 ////////////////////////////////////////////////////////////////////
00056 //     Function: TransformState::Copy Constructor
00057 //       Access: Private
00058 //  Description: TransformStates are not meant to be copied.
00059 ////////////////////////////////////////////////////////////////////
00060 TransformState::
00061 TransformState(const TransformState &) {
00062   nassertv(false);
00063 }
00064 
00065 ////////////////////////////////////////////////////////////////////
00066 //     Function: TransformState::Copy Assignment Operator
00067 //       Access: Private
00068 //  Description: TransformStates are not meant to be copied.
00069 ////////////////////////////////////////////////////////////////////
00070 void TransformState::
00071 operator = (const TransformState &) {
00072   nassertv(false);
00073 }
00074 
00075 ////////////////////////////////////////////////////////////////////
00076 //     Function: TransformState::Destructor
00077 //       Access: Public, Virtual
00078 //  Description: The destructor is responsible for removing the
00079 //               TransformState from the global set if it is there.
00080 ////////////////////////////////////////////////////////////////////
00081 TransformState::
00082 ~TransformState() {
00083   // We'd better not call the destructor twice on a particular object.
00084   nassertv(!is_destructing());
00085   set_destructing();
00086 
00087   // Free the inverse matrix computation, if it has been stored.
00088   if (_inv_mat != (LMatrix4f *)NULL) {
00089     delete _inv_mat;
00090   }
00091 
00092   // Remove the deleted TransformState object from the global pool.
00093   if (_saved_entry != _states->end()) {
00094     nassertv(_states->find(this) == _saved_entry);
00095     _states->erase(_saved_entry);
00096     _saved_entry = _states->end();
00097   }
00098 
00099   // Now make sure we clean up all other floating pointers to the
00100   // TransformState.  These may be scattered around in the various
00101   // CompositionCaches from other TransformState objects.
00102 
00103   // Fortunately, since we added CompositionCache records in pairs, we
00104   // know exactly the set of TransformState objects that have us in their
00105   // cache: it's the same set of TransformState objects that we have in
00106   // our own cache.
00107 
00108   // We do need to put considerable thought into this loop, because as
00109   // we clear out cache entries we'll cause other TransformState
00110   // objects to destruct, which could cause things to get pulled out
00111   // of our own _composition_cache map.  We want to allow this (so
00112   // that we don't encounter any just-destructed pointers in our
00113   // cache), but we don't want to get bitten by this cascading effect.
00114   // Instead of walking through the map from beginning to end,
00115   // therefore, we just pull out the first one each time, and erase
00116   // it.
00117 
00118   // There are lots of ways to do this loop wrong.  Be very careful if
00119   // you need to modify it for any reason.
00120   while (!_composition_cache.empty()) {
00121     CompositionCache::iterator ci = _composition_cache.begin();
00122 
00123     // It is possible that the "other" TransformState object is
00124     // currently within its own destructor.  We therefore can't use a
00125     // PT() to hold its pointer; that could end up calling its
00126     // destructor twice.  Fortunately, we don't need to hold its
00127     // reference count to ensure it doesn't destruct while we process
00128     // this loop; as long as we ensure that no *other* TransformState
00129     // objects destruct, there will be no reason for that one to.
00130     TransformState *other = (TransformState *)(*ci).first;
00131 
00132     // We should never have a reflexive entry in this map.  If we
00133     // do, something got screwed up elsewhere.
00134     nassertv(other != this);
00135 
00136     // We hold a copy of the composition result to ensure that the
00137     // result TransformState object (if there is one) doesn't
00138     // destruct.
00139     Composition comp = (*ci).second;
00140 
00141     // Now we can remove the element from our cache.  We do this now,
00142     // rather than later, before any other TransformState objects have
00143     // had a chance to destruct, so we are confident that our iterator
00144     // is still valid.
00145     _composition_cache.erase(ci);
00146 
00147     CompositionCache::iterator oci = other->_composition_cache.find(this);
00148 
00149     // We may or may not still be listed in the other's cache (it
00150     // might be halfway through pulling entries out, from within its
00151     // own destructor).
00152     if (oci != other->_composition_cache.end()) {
00153       // Hold a copy of the other composition result, too.
00154       Composition ocomp = (*oci).second;
00155       
00156       // Now we're holding a reference count to both computed
00157       // results, so no objects will be tempted to destruct while we
00158       // erase the other cache entry.
00159       other->_composition_cache.erase(oci);
00160     }
00161 
00162     // It's finally safe to let our held pointers go away.  This may
00163     // have cascading effects as other TransformState objects are
00164     // destructed, but there will be no harm done if they destruct
00165     // now.
00166   }
00167 
00168   // A similar bit of code for the invert cache.
00169   while (!_invert_composition_cache.empty()) {
00170     CompositionCache::iterator ci = _invert_composition_cache.begin();
00171     TransformState *other = (TransformState *)(*ci).first;
00172     nassertv(other != this);
00173     Composition comp = (*ci).second;
00174     _invert_composition_cache.erase(ci);
00175     CompositionCache::iterator oci = 
00176       other->_invert_composition_cache.find(this);
00177     if (oci != other->_invert_composition_cache.end()) {
00178       Composition ocomp = (*oci).second;
00179       other->_invert_composition_cache.erase(oci);
00180     }
00181   }
00182 
00183   // Also, if we called compose(this) at some point and the return
00184   // value was something other than this, we need to decrement the
00185   // associated reference count.
00186   if (_self_compose != (TransformState *)NULL && _self_compose != this) {
00187     unref_delete((TransformState *)_self_compose);
00188   }
00189 
00190   // If this was true at the beginning of the destructor, but is no
00191   // longer true now, probably we've been double-deleted.
00192   nassertv(get_ref_count() == 0);
00193 }
00194 
00195 ////////////////////////////////////////////////////////////////////
00196 //     Function: TransformState::operator <
00197 //       Access: Public
00198 //  Description: Provides an arbitrary ordering among all unique
00199 //               TransformStates, so we can store the essentially
00200 //               different ones in a big set and throw away the rest.
00201 //
00202 //               This method is not needed outside of the TransformState
00203 //               class because all equivalent TransformState objects are
00204 //               guaranteed to share the same pointer; thus, a pointer
00205 //               comparison is always sufficient.
00206 ////////////////////////////////////////////////////////////////////
00207 bool TransformState::
00208 operator < (const TransformState &other) const {
00209   static const int significant_flags = 
00210     (F_is_invalid | F_is_identity | F_components_given | F_hpr_given);
00211 
00212   int flags = (_flags & significant_flags);
00213   int other_flags = (other._flags & significant_flags);
00214   if (flags != other_flags) {
00215     return flags < other_flags;
00216   }
00217 
00218   if ((_flags & (F_is_invalid | F_is_identity)) != 0) {
00219     // All invalid transforms are equivalent to each other, and all
00220     // identity transforms are equivalent to each other.
00221     return 0;
00222   }
00223 
00224   if ((_flags & (F_components_given | F_hpr_given | F_quat_given)) == 
00225       (F_components_given | F_hpr_given | F_quat_given)) {
00226     // If the transform was specified componentwise, compare them
00227     // componentwise.
00228     int c = _pos.compare_to(other._pos);
00229     if (c != 0) {
00230       return c < 0;
00231     }
00232 
00233     if ((_flags & F_hpr_given) != 0) {
00234       c = _hpr.compare_to(other._hpr);
00235       if (c != 0) {
00236         return c < 0;
00237       }
00238     } else if ((_flags & F_quat_given) != 0) {
00239       c = _quat.compare_to(other._quat);
00240       if (c != 0) {
00241         return c < 0;
00242       }
00243     }
00244 
00245     c = _scale.compare_to(other._scale);
00246     return c < 0;
00247   }
00248 
00249   /*
00250   // Otherwise, compare the matrices.
00251   return get_mat() < other.get_mat();
00252   */
00253 
00254   // On second thought, we don't gain a lot of benefit by going
00255   // through all the work of comparing different transforms by matrix.
00256   // Doing so ensures that two differently-computed transforms that
00257   // happen to encode the same matrix (an unlikely occurrence) will be
00258   // collapsed into a single pointer (a tiny benefit).  We're better
00259   // off not paying the cost of this comparison, and just assuming
00260   // that any two differently-computed transforms are essentially
00261   // different.
00262   return (this < &other);
00263 }
00264 
00265 ////////////////////////////////////////////////////////////////////
00266 //     Function: TransformState::make_identity
00267 //       Access: Published, Static
00268 //  Description: Constructs an identity transform.
00269 ////////////////////////////////////////////////////////////////////
00270 CPT(TransformState) TransformState::
00271 make_identity() {
00272   // The identity state is asked for so often, we make it a special case
00273   // and store a pointer forever once we find it the first time.
00274   if (_identity_state == (TransformState *)NULL) {
00275     TransformState *state = new TransformState;
00276     _identity_state = return_new(state);
00277   }
00278 
00279   return _identity_state;
00280 }
00281 
00282 ////////////////////////////////////////////////////////////////////
00283 //     Function: TransformState::make_invalid
00284 //       Access: Published, Static
00285 //  Description: Constructs an invalid transform; for instance, the
00286 //               result of inverting a singular matrix.
00287 ////////////////////////////////////////////////////////////////////
00288 CPT(TransformState) TransformState::
00289 make_invalid() {
00290   TransformState *state = new TransformState;
00291   state->_flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known;
00292   return return_new(state);
00293 }
00294 
00295 ////////////////////////////////////////////////////////////////////
00296 //     Function: TransformState::make_pos_hpr_scale
00297 //       Access: Published, Static
00298 //  Description: Makes a new TransformState with the specified
00299 //               components.
00300 ////////////////////////////////////////////////////////////////////
00301 CPT(TransformState) TransformState::
00302 make_pos_hpr_scale(const LVecBase3f &pos, const LVecBase3f &hpr, 
00303                    const LVecBase3f &scale) {
00304   // Make a special-case check for the identity transform.
00305   if (pos == LVecBase3f(0.0f, 0.0f, 0.0f) &&
00306       hpr == LVecBase3f(0.0f, 0.0f, 0.0f) &&
00307       scale == LVecBase3f(1.0f, 1.0f, 1.0f)) {
00308     return make_identity();
00309   }
00310 
00311   TransformState *state = new TransformState;
00312   state->_pos = pos;
00313   state->_hpr = hpr;
00314   state->_scale = scale;
00315   state->_flags = F_components_given | F_hpr_given | F_components_known | F_hpr_known | F_has_components;
00316   state->check_uniform_scale();
00317   return return_new(state);
00318 }
00319 
00320 ////////////////////////////////////////////////////////////////////
00321 //     Function: TransformState::make_pos_quat_scale
00322 //       Access: Published, Static
00323 //  Description: Makes a new TransformState with the specified
00324 //               components.
00325 ////////////////////////////////////////////////////////////////////
00326 CPT(TransformState) TransformState::
00327 make_pos_quat_scale(const LVecBase3f &pos, const LQuaternionf &quat, 
00328                     const LVecBase3f &scale) {
00329   // Make a special-case check for the identity transform.
00330   if (pos == LVecBase3f(0.0f, 0.0f, 0.0f) &&
00331       quat == LQuaternionf::ident_quat() &&
00332       scale == LVecBase3f(1.0f, 1.0f, 1.0f)) {
00333     return make_identity();
00334   }
00335 
00336   TransformState *state = new TransformState;
00337   state->_pos = pos;
00338   state->_quat = quat;
00339   state->_scale = scale;
00340   state->_flags = F_components_given | F_quat_given | F_components_known | F_quat_known | F_has_components;
00341   state->check_uniform_scale();
00342   return return_new(state);
00343 }
00344 
00345 ////////////////////////////////////////////////////////////////////
00346 //     Function: TransformState::make_mat
00347 //       Access: Published, Static
00348 //  Description: Makes a new TransformState with the specified
00349 //               transformation matrix.
00350 ////////////////////////////////////////////////////////////////////
00351 CPT(TransformState) TransformState::
00352 make_mat(const LMatrix4f &mat) {
00353   // Make a special-case check for the identity matrix.
00354   if (mat == LMatrix4f::ident_mat()) {
00355     return make_identity();
00356   }
00357 
00358   TransformState *state = new TransformState;
00359   state->_mat = mat;
00360   state->_flags = F_mat_known;
00361   return return_new(state);
00362 }
00363 
00364 ////////////////////////////////////////////////////////////////////
00365 //     Function: TransformState::set_pos
00366 //       Access: Published
00367 //  Description: Returns a new TransformState object that represents the
00368 //               original TransformState with its pos component
00369 //               replaced with the indicated value.
00370 ////////////////////////////////////////////////////////////////////
00371 CPT(TransformState) TransformState::
00372 set_pos(const LVecBase3f &pos) const {
00373   nassertr(!is_invalid(), this);
00374   if (is_identity() || components_given()) {
00375     // If we started with a componentwise transform, we keep it that
00376     // way.
00377     if (quat_given()) {
00378       return make_pos_quat_scale(pos, get_quat(), get_scale());
00379     } else {
00380       return make_pos_hpr_scale(pos, get_hpr(), get_scale());
00381     }
00382 
00383   } else {
00384     // Otherwise, we have a matrix transform, and we keep it that way.
00385     LMatrix4f mat = get_mat();
00386     mat.set_row(3, pos);
00387     return make_mat(mat);
00388   }
00389 }
00390 
00391 ////////////////////////////////////////////////////////////////////
00392 //     Function: TransformState::set_hpr
00393 //       Access: Published
00394 //  Description: Returns a new TransformState object that represents the
00395 //               original TransformState with its rotation component
00396 //               replaced with the indicated value, if possible.
00397 ////////////////////////////////////////////////////////////////////
00398 CPT(TransformState) TransformState::
00399 set_hpr(const LVecBase3f &hpr) const {
00400   nassertr(!is_invalid(), this);
00401   //  nassertr(has_components(), this);
00402   return make_pos_hpr_scale(get_pos(), hpr, get_scale());
00403 }
00404 
00405 ////////////////////////////////////////////////////////////////////
00406 //     Function: TransformState::set_quat
00407 //       Access: Published
00408 //  Description: Returns a new TransformState object that represents the
00409 //               original TransformState with its rotation component
00410 //               replaced with the indicated value, if possible.
00411 ////////////////////////////////////////////////////////////////////
00412 CPT(TransformState) TransformState::
00413 set_quat(const LQuaternionf &quat) const {
00414   nassertr(!is_invalid(), this);
00415   //  nassertr(has_components(), this);
00416   return make_pos_quat_scale(get_pos(), quat, get_scale());
00417 }
00418 
00419 ////////////////////////////////////////////////////////////////////
00420 //     Function: TransformState::set_scale
00421 //       Access: Published
00422 //  Description: Returns a new TransformState object that represents the
00423 //               original TransformState with its scale component
00424 //               replaced with the indicated value, if possible.
00425 ////////////////////////////////////////////////////////////////////
00426 CPT(TransformState) TransformState::
00427 set_scale(const LVecBase3f &scale) const {
00428   nassertr(!is_invalid(), this);
00429   //  nassertr(has_components(), this);
00430   if (quat_given()) {
00431     return make_pos_quat_scale(get_pos(), get_quat(), scale);
00432   } else {
00433     return make_pos_hpr_scale(get_pos(), get_hpr(), scale);
00434   }
00435 }
00436 
00437 ////////////////////////////////////////////////////////////////////
00438 //     Function: TransformState::compose
00439 //       Access: Published
00440 //  Description: Returns a new TransformState object that represents the
00441 //               composition of this state with the other state.
00442 //
00443 //               The result of this operation is cached, and will be
00444 //               retained as long as both this TransformState object and
00445 //               the other TransformState object continue to exist.
00446 //               Should one of them destruct, the cached entry will be
00447 //               removed, and its pointer will be allowed to destruct
00448 //               as well.
00449 ////////////////////////////////////////////////////////////////////
00450 CPT(TransformState) TransformState::
00451 compose(const TransformState *other) const {
00452   // This method isn't strictly const, because it updates the cache,
00453   // but we pretend that it is because it's only a cache which is
00454   // transparent to the rest of the interface.
00455 
00456   // We handle identity as a trivial special case.
00457   if (is_identity()) {
00458     return other;
00459   }
00460   if (other->is_identity()) {
00461     return this;
00462   }
00463 
00464   // If either transform is invalid, the result is invalid.
00465   if (is_invalid()) {
00466     return this;
00467   }
00468   if (other->is_invalid()) {
00469     return other;
00470   }
00471 
00472   if (other == this) {
00473     // compose(this) has to be handled as a special case, because the
00474     // caching problem is so different.
00475     if (_self_compose != (TransformState *)NULL) {
00476       return _self_compose;
00477     }
00478     CPT(TransformState) result = do_compose(this);
00479     ((TransformState *)this)->_self_compose = result;
00480 
00481     if (result != (const TransformState *)this) {
00482       // If the result of compose(this) is something other than this,
00483       // explicitly increment the reference count.  We have to be sure
00484       // to decrement it again later, in our destructor.
00485       _self_compose->ref();
00486 
00487       // (If the result was just this again, we still store the
00488       // result, but we don't increment the reference count, since
00489       // that would be a self-referential leak.  What a mess this is.)
00490     }
00491     return _self_compose;
00492   }
00493 
00494   // Is this composition already cached?
00495   CompositionCache::const_iterator ci = _composition_cache.find(other);
00496   if (ci != _composition_cache.end()) {
00497     const Composition &comp = (*ci).second;
00498     if (comp._result == (const TransformState *)NULL) {
00499       // Well, it wasn't cached already, but we already had an entry
00500       // (probably created for the reverse direction), so use the same
00501       // entry to store the new result.
00502       ((Composition &)comp)._result = do_compose(other);
00503     }
00504     // Here's the cache!
00505     return comp._result;
00506   }
00507 
00508   // We need to make a new cache entry, both in this object and in the
00509   // other object.  We make both records so the other TransformState
00510   // object will know to delete the entry from this object when it
00511   // destructs, and vice-versa.
00512 
00513   // The cache entry in this object is the only one that indicates the
00514   // result; the other will be NULL for now.
00515   CPT(TransformState) result = do_compose(other);
00516 
00517   ((TransformState *)other)->_composition_cache[this]._result = NULL;
00518   ((TransformState *)this)->_composition_cache[other]._result = result;
00519 
00520   return result;
00521 }
00522 
00523 ////////////////////////////////////////////////////////////////////
00524 //     Function: TransformState::invert_compose
00525 //       Access: Published
00526 //  Description: Returns a new TransformState object that represents the
00527 //               composition of this state's inverse with the other
00528 //               state.
00529 //
00530 //               This is similar to compose(), but is particularly
00531 //               useful for computing the relative state of a node as
00532 //               viewed from some other node.
00533 ////////////////////////////////////////////////////////////////////
00534 CPT(TransformState) TransformState::
00535 invert_compose(const TransformState *other) const {
00536   // This method isn't strictly const, because it updates the cache,
00537   // but we pretend that it is because it's only a cache which is
00538   // transparent to the rest of the interface.
00539 
00540   // We handle identity as a trivial special case.
00541   if (is_identity()) {
00542     return other;
00543   }
00544   // Unlike compose(), the case of other->is_identity() is not quite as
00545   // trivial for invert_compose().
00546 
00547   // If either transform is invalid, the result is invalid.
00548   if (is_invalid()) {
00549     return this;
00550   }
00551   if (other->is_invalid()) {
00552     return other;
00553   }
00554 
00555   if (other == this) {
00556     // a->invert_compose(a) always produces identity.
00557     return make_identity();
00558   }
00559 
00560   // Is this composition already cached?
00561   CompositionCache::const_iterator ci = _invert_composition_cache.find(other);
00562   if (ci != _invert_composition_cache.end()) {
00563     const Composition &comp = (*ci).second;
00564     if (comp._result == (const TransformState *)NULL) {
00565       // Well, it wasn't cached already, but we already had an entry
00566       // (probably created for the reverse direction), so use the same
00567       // entry to store the new result.
00568       ((Composition &)comp)._result = do_invert_compose(other);
00569     }
00570     // Here's the cache!
00571     return comp._result;
00572   }
00573 
00574   // We need to make a new cache entry, both in this object and in the
00575   // other object.  We make both records so the other TransformState
00576   // object will know to delete the entry from this object when it
00577   // destructs, and vice-versa.
00578 
00579   // The cache entry in this object is the only one that indicates the
00580   // result; the other will be NULL for now.
00581   CPT(TransformState) result = do_invert_compose(other);
00582 
00583   ((TransformState *)other)->_invert_composition_cache[this]._result = NULL;
00584   ((TransformState *)this)->_invert_composition_cache[other]._result = result;
00585 
00586   return result;
00587 }
00588 
00589 ////////////////////////////////////////////////////////////////////
00590 //     Function: TransformState::output
00591 //       Access: Published, Virtual
00592 //  Description: 
00593 ////////////////////////////////////////////////////////////////////
00594 void TransformState::
00595 output(ostream &out) const {
00596   out << "T:";
00597   if (is_invalid()) {
00598     out << "(invalid)";
00599 
00600   } else if (is_identity()) {
00601     out << "(identity)";
00602 
00603   } else if (has_components()) {
00604     bool output_hpr = !get_hpr().almost_equal(LVecBase3f(0.0f, 0.0f, 0.0f));
00605 
00606     if (!components_given()) {
00607       // A leading "m" indicates the transform was described as a full
00608       // matrix, and we are decomposing it for the benefit of the
00609       // user.
00610       out << "m";
00611 
00612     } else if (output_hpr && quat_given()) {
00613       // A leading "q" indicates that the pos and scale are exactly as
00614       // specified, but the rotation was described as a quaternion,
00615       // and we are decomposing that to hpr for the benefit of the
00616       // user.
00617       out << "q";
00618     }
00619 
00620     char lead = '(';
00621     if (!get_pos().almost_equal(LVecBase3f(0.0f, 0.0f, 0.0f))) {
00622       out << lead << "pos " << get_pos();
00623       lead = ' ';
00624     }
00625     if (output_hpr) {
00626       out << lead << "hpr " << get_hpr();
00627       lead = ' ';
00628     }
00629     if (!get_scale().almost_equal(LVecBase3f(1.0f, 1.0f, 1.0f))) {
00630       if (has_uniform_scale()) {
00631         out << lead << "scale " << get_uniform_scale();
00632         lead = ' ';
00633       } else {
00634         out << lead << "scale " << get_scale();
00635         lead = ' ';
00636       }
00637     }
00638     if (lead == '(') {
00639       out << "(almost identity)";
00640     } else {
00641       out << ")";
00642     }
00643 
00644   } else {
00645     out << get_mat();
00646   }
00647 }
00648 
00649 
00650 ////////////////////////////////////////////////////////////////////
00651 //     Function: TransformState::write
00652 //       Access: Published, Virtual
00653 //  Description: 
00654 ////////////////////////////////////////////////////////////////////
00655 void TransformState::
00656 write(ostream &out, int indent_level) const {
00657   indent(out, indent_level) << *this << "\n";
00658 }
00659 
00660 ////////////////////////////////////////////////////////////////////
00661 //     Function: TransformState::get_num_states
00662 //       Access: Published, Static
00663 //  Description: Returns the total number of unique TransformState
00664 //               objects allocated in the world.  This will go up and
00665 //               down during normal operations.
00666 ////////////////////////////////////////////////////////////////////
00667 int TransformState::
00668 get_num_states() {
00669   if (_states == (States *)NULL) {
00670     return 0;
00671   }
00672   return _states->size();
00673 }
00674 
00675 ////////////////////////////////////////////////////////////////////
00676 //     Function: TransformState::get_num_unused_states
00677 //       Access: Published, Static
00678 //  Description: Returns the total number of TransformState objects
00679 //               that have been allocated but have no references
00680 //               outside of the internal TransformState map.
00681 ////////////////////////////////////////////////////////////////////
00682 int TransformState::
00683 get_num_unused_states() {
00684   if (_states == (States *)NULL) {
00685     return 0;
00686   }
00687 
00688   int num_unused = 0;
00689 
00690   // First, we need to count the number of times each TransformState
00691   // object is recorded in the cache.
00692   typedef pmap<const TransformState *, int> StateCount;
00693   StateCount state_count;
00694 
00695   States::iterator si;
00696   for (si = _states->begin(); si != _states->end(); ++si) {
00697     const TransformState *state = (*si);
00698 
00699     CompositionCache::const_iterator ci;
00700     for (ci = state->_composition_cache.begin();
00701          ci != state->_composition_cache.end();
00702          ++ci) {
00703       const TransformState *result = (*ci).second._result;
00704       if (result != (const TransformState *)NULL) {
00705         // Here's a TransformState that's recorded in the cache.
00706         // Count it.
00707         pair<StateCount::iterator, bool> ir =
00708           state_count.insert(StateCount::value_type(result, 1));
00709         if (!ir.second) {
00710           // If the above insert operation fails, then it's already in
00711           // the cache; increment its value.
00712           (*(ir.first)).second++;
00713         }
00714       }
00715     }
00716     for (ci = state->_invert_composition_cache.begin();
00717          ci != state->_invert_composition_cache.end();
00718          ++ci) {
00719       const TransformState *result = (*ci).second._result;
00720       if (result != (const TransformState *)NULL) {
00721         pair<StateCount::iterator, bool> ir =
00722           state_count.insert(StateCount::value_type(result, 1));
00723         if (!ir.second) {
00724           (*(ir.first)).second++;
00725         }
00726       }
00727     }
00728 
00729     // Finally, check the self_compose field, which might be reference
00730     // counted too.
00731     if (state->_self_compose != (const TransformState *)NULL &&
00732         state->_self_compose != state) {
00733       const TransformState *result = state->_self_compose;
00734       if (result != (const TransformState *)NULL) {
00735         pair<StateCount::iterator, bool> ir =
00736           state_count.insert(StateCount::value_type(result, 1));
00737         if (!ir.second) {
00738           (*(ir.first)).second++;
00739         }
00740       }
00741     }
00742 
00743   }
00744 
00745   // Now that we have the appearance count of each TransformState
00746   // object, we can tell which ones are unreferenced outside of the
00747   // TransformState cache, by comparing these to the reference counts.
00748   StateCount::iterator sci;
00749   for (sci = state_count.begin(); sci != state_count.end(); ++sci) {
00750     const TransformState *state = (*sci).first;
00751     int count = (*sci).second;
00752     nassertr(count <= state->get_ref_count(), num_unused);
00753     if (count == state->get_ref_count()) {
00754       num_unused++;
00755     }
00756   }
00757 
00758   return num_unused;
00759 }
00760 
00761 ////////////////////////////////////////////////////////////////////
00762 //     Function: TransformState::clear_cache
00763 //       Access: Published, Static
00764 //  Description: Empties the cache of composed TransformStates.  This
00765 //               makes every TransformState forget what results when
00766 //               it is composed with other TransformStates.
00767 //
00768 //               This will eliminate any TransformState objects that
00769 //               have been allocated but have no references outside of
00770 //               the internal TransformState map.  It will not
00771 //               eliminate TransformState objects that are still in
00772 //               use.
00773 //
00774 //               Normally, TransformState objects will remove
00775 //               themselves from the interal map when their reference
00776 //               counts go to 0, but since circular references are
00777 //               possible there may be some cycles that cannot remove
00778 //               themselves.  Calling this function from time to time
00779 //               will ensure there is no wasteful memory leakage, but
00780 //               calling it too often may result in decreased
00781 //               performance as the cache is forced to be recomputed.
00782 //
00783 //               The return value is the number of TransformStates
00784 //               freed by this operation.
00785 ////////////////////////////////////////////////////////////////////
00786 int TransformState::
00787 clear_cache() {
00788   if (_states == (States *)NULL) {
00789     return 0;
00790   }
00791 
00792   int orig_size = _states->size();
00793 
00794   // First, we need to copy the entire set of transforms to a
00795   // temporary vector, reference-counting each object.  That way we
00796   // can walk through the copy, without fear of dereferencing (and
00797   // deleting) the objects in the map as we go.
00798   {
00799     typedef pvector< CPT(TransformState) > TempStates;
00800     TempStates temp_states;
00801     temp_states.reserve(orig_size);
00802 
00803     copy(_states->begin(), _states->end(),
00804          back_inserter(temp_states));
00805 
00806     // Now it's safe to walk through the list, destroying the cache
00807     // within each object as we go.  Nothing will be destructed till
00808     // we're done.
00809     TempStates::iterator ti;
00810     for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) {
00811       TransformState *state = (TransformState *)(*ti).p();
00812       state->_composition_cache.clear();
00813       state->_invert_composition_cache.clear();
00814       if (state->_self_compose != (TransformState *)NULL && 
00815           state->_self_compose != state) {
00816         unref_delete((TransformState *)state->_self_compose);
00817         state->_self_compose = (TransformState *)NULL;
00818       }
00819     }
00820 
00821     // Once this block closes and the temp_states object goes away,
00822     // all the destruction will begin.  Anything whose reference was
00823     // held only within the various objects' caches will go away.
00824   }
00825 
00826   int new_size = _states->size();
00827   return orig_size - new_size;
00828 }
00829 
00830 ////////////////////////////////////////////////////////////////////
00831 //     Function: TransformState::return_new
00832 //       Access: Private, Static
00833 //  Description: This function is used to share a common TransformState
00834 //               pointer for all equivalent TransformState objects.
00835 //
00836 //               See the similar logic in RenderState.  The idea is to
00837 //               create a new TransformState object and pass it
00838 //               through this function, which will share the pointer
00839 //               with a previously-created TransformState object if it
00840 //               is equivalent.
00841 ////////////////////////////////////////////////////////////////////
00842 CPT(TransformState) TransformState::
00843 return_new(TransformState *state) {
00844   nassertr(state != (TransformState *)NULL, state);
00845 
00846   // This should be a newly allocated pointer, not one that was used
00847   // for anything else.
00848   nassertr(state->_saved_entry == _states->end(), state);
00849 
00850   // Save the state in a local PointerTo so that it will be freed at
00851   // the end of this function if no one else uses it.
00852   CPT(TransformState) pt_state = state;
00853 
00854   pair<States::iterator, bool> result = _states->insert(state);
00855   if (result.second) {
00856     // The state was inserted; save the iterator and return the
00857     // input state.
00858     state->_saved_entry = result.first;
00859     return pt_state;
00860   }
00861 
00862   // The state was not inserted; there must be an equivalent one
00863   // already in the set.  Return that one.
00864   return *(result.first);
00865 }
00866 
00867 ////////////////////////////////////////////////////////////////////
00868 //     Function: TransformState::do_compose
00869 //       Access: Private
00870 //  Description: The private implemention of compose(); this actually
00871 //               composes two TransformStates, without bothering with the
00872 //               cache.
00873 ////////////////////////////////////////////////////////////////////
00874 CPT(TransformState) TransformState::
00875 do_compose(const TransformState *other) const {
00876   nassertr((_flags & F_is_invalid) == 0, this);
00877   nassertr((other->_flags & F_is_invalid) == 0, other);
00878 
00879   if (compose_componentwise && 
00880       has_uniform_scale() && 
00881       ((components_given() && other->has_components()) ||
00882        (other->components_given() && has_components()))) {
00883     // We will do this operation componentwise if *either* transform
00884     // was given componentwise (and there is no non-uniform scale in
00885     // the way).
00886 
00887     LVecBase3f pos = get_pos();
00888     LQuaternionf quat = get_quat();
00889     float scale = get_uniform_scale();
00890 
00891     pos += quat.xform(other->get_pos()) * scale;
00892     quat = other->get_quat() * quat;
00893     quat.normalize();
00894     LVecBase3f new_scale = other->get_scale() * scale;
00895 
00896     CPT(TransformState) result =
00897       make_pos_quat_scale(pos, quat, new_scale);
00898 
00899 #ifndef NDEBUG
00900     if (paranoid_compose) {
00901       // Now verify against the matrix.
00902       LMatrix4f new_mat = other->get_mat() * get_mat();
00903       if (!new_mat.almost_equal(result->get_mat(), 0.05)) {
00904         CPT(TransformState) correct = make_mat(new_mat);
00905         pgraph_cat.warning()
00906           << "Componentwise composition of " << *this << " and " << *other
00907           << " produced:\n"
00908           << *result << "\n  instead of:\n" << *correct << "\n";
00909         result = correct;
00910       }
00911     }
00912 #endif  // NDEBUG
00913 
00914     return result;
00915   }
00916 
00917   // Do the operation with matrices.
00918   LMatrix4f new_mat = other->get_mat() * get_mat();
00919   return make_mat(new_mat);
00920 }
00921 
00922 ////////////////////////////////////////////////////////////////////
00923 //     Function: TransformState::do_invert_compose
00924 //       Access: Private
00925 //  Description: The private implemention of invert_compose().
00926 ////////////////////////////////////////////////////////////////////
00927 CPT(TransformState) TransformState::
00928 do_invert_compose(const TransformState *other) const {
00929   nassertr((_flags & F_is_invalid) == 0, this);
00930   nassertr((other->_flags & F_is_invalid) == 0, other);
00931 
00932   if (compose_componentwise && 
00933       has_uniform_scale() && 
00934       ((components_given() && other->has_components()) ||
00935        (other->components_given() && has_components()))) {
00936     // We will do this operation componentwise if *either* transform
00937     // was given componentwise (and there is no non-uniform scale in
00938     // the way).
00939 
00940     LVecBase3f pos = get_pos();
00941     LQuaternionf quat = get_quat();
00942     float scale = get_uniform_scale();
00943 
00944     // First, invert our own transform.
00945     if (scale == 0.0f) {
00946       ((TransformState *)this)->_flags |= F_is_singular | F_singular_known;
00947       return make_invalid();
00948     }
00949     scale = 1.0f / scale;
00950     quat.invert_in_place();
00951     pos = quat.xform(-pos) * scale;
00952     LVecBase3f new_scale(scale, scale, scale);
00953 
00954     // Now compose the inverted transform with the other transform.
00955     if (!other->is_identity()) {
00956       pos += quat.xform(other->get_pos()) * scale;
00957       quat = other->get_quat() * quat;
00958       quat.normalize();
00959       new_scale = other->get_scale() * scale;
00960     }
00961 
00962     CPT(TransformState) result =
00963       make_pos_quat_scale(pos, quat, new_scale);
00964 
00965 #ifndef NDEBUG
00966     if (paranoid_compose) {
00967       // Now verify against the matrix.
00968       if (is_singular()) {
00969         pgraph_cat.warning()
00970           << "Unexpected singular matrix found for " << *this << "\n";
00971       } else {
00972         nassertr(_inv_mat != (LMatrix4f *)NULL, make_invalid());
00973         LMatrix4f new_mat = other->get_mat() * (*_inv_mat);
00974         if (!new_mat.almost_equal(result->get_mat(), 0.05)) {
00975           CPT(TransformState) correct = make_mat(new_mat);
00976           pgraph_cat.warning()
00977             << "Componentwise invert-composition of " << *this << " and " << *other
00978             << " produced:\n"
00979             << *result << "\n  instead of:\n" << *correct << "\n";
00980           result = correct;
00981         }
00982       }
00983     }
00984 #endif  // NDEBUG
00985 
00986     return result;
00987   }
00988 
00989   if (is_singular()) {
00990     return make_invalid();
00991   }
00992 
00993   // Now that is_singular() has returned false, we can assume that
00994   // _inv_mat has been allocated and filled in.
00995   nassertr(_inv_mat != (LMatrix4f *)NULL, make_invalid());
00996 
00997   if (other->is_identity()) {
00998     return make_mat(*_inv_mat);
00999   } else {
01000     return make_mat(other->get_mat() * (*_inv_mat));
01001   }
01002 }
01003 
01004 ////////////////////////////////////////////////////////////////////
01005 //     Function: TransformState::calc_singular
01006 //       Access: Private
01007 //  Description: Determines whether the transform is singular (i.e. it
01008 //               scales to zero, and has no inverse).
01009 ////////////////////////////////////////////////////////////////////
01010 void TransformState::
01011 calc_singular() {
01012   nassertv((_flags & F_is_invalid) == 0);
01013 
01014   // We determine if a matrix is singular by attempting to invert it
01015   // (and we save the result of this invert operation for a subsequent
01016   // do_invert_compose() call, which is almost certain to be made if
01017   // someone is asking whether we're singular).
01018 
01019   // This should be NULL if no one has called calc_singular() yet.
01020   nassertv(_inv_mat == (LMatrix4f *)NULL);
01021   _inv_mat = new LMatrix4f;
01022   bool inverted = _inv_mat->invert_from(get_mat());
01023 
01024   if (!inverted) {
01025     _flags |= F_is_singular;
01026     delete _inv_mat;
01027     _inv_mat = (LMatrix4f *)NULL;
01028   }
01029   _flags |= F_singular_known;
01030 }
01031 
01032 ////////////////////////////////////////////////////////////////////
01033 //     Function: TransformState::calc_components
01034 //       Access: Private
01035 //  Description: Derives the components from the matrix, if possible.
01036 ////////////////////////////////////////////////////////////////////
01037 void TransformState::
01038 calc_components() {
01039   nassertv((_flags & F_is_invalid) == 0);
01040   if ((_flags & F_is_identity) != 0) {
01041     _scale.set(1.0f, 1.0f, 1.0f);
01042     _hpr.set(0.0f, 0.0f, 0.0f);
01043     _quat = LQuaternionf::ident_quat();
01044     _pos.set(0.0f, 0.0f, 0.0f);
01045     _flags |= F_has_components | F_components_known | F_hpr_known | F_quat_known | F_uniform_scale;
01046 
01047   } else {
01048     // If we don't have components and we're not identity, the only
01049     // other explanation is that we were constructed via a matrix.
01050     nassertv((_flags & F_mat_known) != 0);
01051 
01052     const LMatrix4f &mat = get_mat();
01053     bool possible = decompose_matrix(mat, _scale, _hpr, _pos);
01054     if (!possible) {
01055       // Some matrices can't be decomposed into scale, hpr, pos.  In
01056       // this case, we now know that we cannot compute the components;
01057       // but the closest approximations are stored, at least.
01058       _flags |= F_components_known | F_hpr_known;
01059 
01060     } else {
01061       // Otherwise, we do have the components, or at least the hpr.
01062       _flags |= F_has_components | F_components_known | F_hpr_known;
01063       check_uniform_scale();
01064     }
01065 
01066     // However, we can always get at least the pos.
01067     mat.get_row3(_pos, 3);
01068   }
01069 }
01070 
01071 ////////////////////////////////////////////////////////////////////
01072 //     Function: TransformState::calc_hpr
01073 //       Access: Private
01074 //  Description: Derives the hpr, from the matrix if necessary, or
01075 //               from the quat.
01076 ////////////////////////////////////////////////////////////////////
01077 void TransformState::
01078 calc_hpr() {
01079   nassertv((_flags & F_is_invalid) == 0);
01080   check_components();
01081   if ((_flags & F_hpr_known) == 0) {
01082     // If we don't know the hpr yet, we must have been given a quat.
01083     // Decompose it.
01084     nassertv((_flags & F_quat_known) != 0);
01085     _hpr = _quat.get_hpr();
01086     _flags |= F_hpr_known;
01087   }
01088 }
01089 
01090 ////////////////////////////////////////////////////////////////////
01091 //     Function: TransformState::calc_quat
01092 //       Access: Private
01093 //  Description: Derives the quat from the hpr.
01094 ////////////////////////////////////////////////////////////////////
01095 void TransformState::
01096 calc_quat() {
01097   nassertv((_flags & F_is_invalid) == 0);
01098   check_components();
01099   if ((_flags & F_quat_known) == 0) {
01100     // If we don't know the quat yet, we must have been given a hpr.
01101     // Decompose it.
01102     nassertv((_flags & F_hpr_known) != 0);
01103     _quat.set_hpr(_hpr);
01104     _flags |= F_quat_known;
01105   }
01106 }
01107 
01108 ////////////////////////////////////////////////////////////////////
01109 //     Function: TransformState::calc_mat
01110 //       Access: Private
01111 //  Description: Computes the matrix from the components.
01112 ////////////////////////////////////////////////////////////////////
01113 void TransformState::
01114 calc_mat() {
01115   nassertv((_flags & F_is_invalid) == 0);
01116   if ((_flags & F_is_identity) != 0) {
01117     _mat = LMatrix4f::ident_mat();
01118 
01119   } else {
01120     // If we don't have a matrix and we're not identity, the only
01121     // other explanation is that we were constructed via components.
01122     nassertv((_flags & F_components_known) != 0);
01123     compose_matrix(_mat, _scale, get_hpr(), _pos);
01124   }
01125   _flags |= F_mat_known;
01126 }
01127 
01128 ////////////////////////////////////////////////////////////////////
01129 //     Function: TransformState::register_with_read_factory
01130 //       Access: Public, Static
01131 //  Description: Tells the BamReader how to create objects of type
01132 //               TransformState.
01133 ////////////////////////////////////////////////////////////////////
01134 void TransformState::
01135 register_with_read_factory() {
01136   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
01137 }
01138 
01139 ////////////////////////////////////////////////////////////////////
01140 //     Function: TransformState::write_datagram
01141 //       Access: Public, Virtual
01142 //  Description: Writes the contents of this object to the datagram
01143 //               for shipping out to a Bam file.
01144 ////////////////////////////////////////////////////////////////////
01145 void TransformState::
01146 write_datagram(BamWriter *manager, Datagram &dg) {
01147   TypedWritable::write_datagram(manager, dg);
01148 
01149   if ((_flags & F_is_identity) != 0) {
01150     // Identity, nothing much to that.
01151     int flags = F_is_identity | F_singular_known;
01152     dg.add_uint16(flags);
01153 
01154   } else if ((_flags & F_is_invalid) != 0) {
01155     // Invalid, nothing much to that either.
01156     int flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known;
01157     dg.add_uint16(flags);
01158 
01159   } else if ((_flags & F_components_given) != 0) {
01160     // A component-based transform.
01161     int flags = F_components_given | F_components_known | F_has_components;
01162     if ((_flags & F_quat_given) != 0) {
01163       flags |= (F_quat_given | F_quat_known);
01164     } else if ((_flags & F_hpr_given) != 0) {
01165       flags |= (F_hpr_given | F_hpr_known);
01166     }
01167 
01168     dg.add_uint16(flags);
01169 
01170     _pos.write_datagram(dg);
01171     if ((_flags & F_quat_given) != 0) {
01172       _quat.write_datagram(dg);
01173     } else {
01174       get_hpr().write_datagram(dg);
01175     }
01176     _scale.write_datagram(dg);
01177 
01178   } else {
01179     // A general matrix.
01180     nassertv((_flags & F_mat_known) != 0);
01181     int flags = F_mat_known;
01182     dg.add_uint16(flags);
01183     _mat.write_datagram(dg);
01184   }
01185 }
01186 
01187 ////////////////////////////////////////////////////////////////////
01188 //     Function: TransformState::change_this
01189 //       Access: Public, Static
01190 //  Description: Called immediately after complete_pointers(), this
01191 //               gives the object a chance to adjust its own pointer
01192 //               if desired.  Most objects don't change pointers after
01193 //               completion, but some need to.
01194 //
01195 //               Once this function has been called, the old pointer
01196 //               will no longer be accessed.
01197 ////////////////////////////////////////////////////////////////////
01198 TypedWritable *TransformState::
01199 change_this(TypedWritable *old_ptr, BamReader *manager) {
01200   // First, uniquify the pointer.
01201   TransformState *state = DCAST(TransformState, old_ptr);
01202   CPT(TransformState) pointer = return_new(state);
01203 
01204   // But now we have a problem, since we have to hold the reference
01205   // count and there's no way to return a TypedWritable while still
01206   // holding the reference count!  We work around this by explicitly
01207   // upping the count, and also setting a finalize() callback to down
01208   // it later.
01209   if (pointer == state) {
01210     pointer->ref();
01211     manager->register_finalize(state);
01212   }
01213   
01214   // We have to cast the pointer back to non-const, because the bam
01215   // reader expects that.
01216   return (TransformState *)pointer.p();
01217 }
01218 
01219 ////////////////////////////////////////////////////////////////////
01220 //     Function: TransformState::finalize
01221 //       Access: Public, Virtual
01222 //  Description: Called by the BamReader to perform any final actions
01223 //               needed for setting up the object after all objects
01224 //               have been read and all pointers have been completed.
01225 ////////////////////////////////////////////////////////////////////
01226 void TransformState::
01227 finalize() {
01228   // Unref the pointer that we explicitly reffed in make_from_bam().
01229   unref();
01230 
01231   // We should never get back to zero after unreffing our own count,
01232   // because we expect to have been stored in a pointer somewhere.  If
01233   // we do get to zero, it's a memory leak; the way to avoid this is
01234   // to call unref_delete() above instead of unref(), but this is
01235   // dangerous to do from within a virtual function.
01236   nassertv(get_ref_count() != 0);
01237 }
01238 
01239 ////////////////////////////////////////////////////////////////////
01240 //     Function: TransformState::make_from_bam
01241 //       Access: Protected, Static
01242 //  Description: This function is called by the BamReader's factory
01243 //               when a new object of type TransformState is encountered
01244 //               in the Bam file.  It should create the TransformState
01245 //               and extract its information from the file.
01246 ////////////////////////////////////////////////////////////////////
01247 TypedWritable *TransformState::
01248 make_from_bam(const FactoryParams &params) {
01249   TransformState *state = new TransformState;
01250   DatagramIterator scan;
01251   BamReader *manager;
01252 
01253   parse_params(params, scan, manager);
01254   state->fillin(scan, manager);
01255   manager->register_change_this(change_this, state);
01256 
01257   return state;
01258 }
01259 
01260 ////////////////////////////////////////////////////////////////////
01261 //     Function: TransformState::fillin
01262 //       Access: Protected
01263 //  Description: This internal function is called by make_from_bam to
01264 //               read in all of the relevant data from the BamFile for
01265 //               the new TransformState.
01266 ////////////////////////////////////////////////////////////////////
01267 void TransformState::
01268 fillin(DatagramIterator &scan, BamReader *manager) {
01269   TypedWritable::fillin(scan, manager);
01270 
01271   _flags = scan.get_uint16();
01272 
01273   if ((_flags & F_components_given) != 0) {
01274     // Componentwise transform.
01275     _pos.read_datagram(scan);
01276     if ((_flags & F_quat_given) != 0) {
01277       _quat.read_datagram(scan);
01278     } else {
01279       _hpr.read_datagram(scan);
01280       // Holdover support for bams 4.0 or older: add these bits that
01281       // should have been added when the bam was written.
01282       _flags |= (F_hpr_given | F_hpr_known);
01283     }
01284     _scale.read_datagram(scan);
01285     check_uniform_scale();
01286   }
01287 
01288   if ((_flags & F_mat_known) != 0) {
01289     // General matrix.
01290     _mat.read_datagram(scan);
01291 
01292     if (bams_componentwise) {
01293       // Decompose the matrix if we can, and store it componentwise.
01294       if (has_components()) {
01295         _flags |= F_components_given | F_hpr_given;
01296       }
01297     }
01298   }
01299 }
01300 
01301 ////////////////////////////////////////////////////////////////////
01302 //     Function: EventStoreTransform::output
01303 //       Access: Public, Virtual
01304 //  Description: 
01305 ////////////////////////////////////////////////////////////////////
01306 void EventStoreTransform::
01307 output(ostream &out) const {
01308   out << *_value;
01309 }

Generated on Fri May 2 00:42:30 2003 for Panda by doxygen1.3