// Filename: transformState.cxx // Created by: drose (25Feb02) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://www.panda3d.org/license.txt . // // To contact the maintainers of this program write to // panda3d@yahoogroups.com . // //////////////////////////////////////////////////////////////////// #include "transformState.h" #include "compose_matrix.h" #include "bamReader.h" #include "bamWriter.h" #include "datagramIterator.h" #include "indent.h" #include "compareTo.h" TransformState::States *TransformState::_states = NULL; CPT(TransformState) TransformState::_identity_state; TypeHandle TransformState::_type_handle; //////////////////////////////////////////////////////////////////// // Function: TransformState::Constructor // Access: Protected // Description: Actually, this could be a private constructor, since // no one inherits from TransformState, but gcc gives us a // spurious warning if all constructors are private. //////////////////////////////////////////////////////////////////// TransformState:: TransformState() { if (_states == (States *)NULL) { // Make sure the global _states map is allocated. This only has // to be done once. We could make this map static, but then we // run into problems if anyone creates a TransformState object at // static init time; it also seems to cause problems when the // Panda shared library is unloaded at application exit time. _states = new States; } _saved_entry = _states->end(); _self_compose = (TransformState *)NULL; _flags = F_is_identity | F_singular_known; _inv_mat = (LMatrix4f *)NULL; } //////////////////////////////////////////////////////////////////// // Function: TransformState::Copy Constructor // Access: Private // Description: TransformStates are not meant to be copied. //////////////////////////////////////////////////////////////////// TransformState:: TransformState(const TransformState &) { nassertv(false); } //////////////////////////////////////////////////////////////////// // Function: TransformState::Copy Assignment Operator // Access: Private // Description: TransformStates are not meant to be copied. //////////////////////////////////////////////////////////////////// void TransformState:: operator = (const TransformState &) { nassertv(false); } //////////////////////////////////////////////////////////////////// // Function: TransformState::Destructor // Access: Public, Virtual // Description: The destructor is responsible for removing the // TransformState from the global set if it is there. //////////////////////////////////////////////////////////////////// TransformState:: ~TransformState() { // We'd better not call the destructor twice on a particular object. nassertv(!is_destructing()); set_destructing(); // Free the inverse matrix computation, if it has been stored. if (_inv_mat != (LMatrix4f *)NULL) { delete _inv_mat; _inv_mat = (LMatrix4f *)NULL; } // Remove the deleted TransformState object from the global pool. if (_saved_entry != _states->end()) { nassertv(_states->find(this) == _saved_entry); _states->erase(_saved_entry); _saved_entry = _states->end(); } // Now make sure we clean up all other floating pointers to the // TransformState. These may be scattered around in the various // CompositionCaches from other TransformState objects. // Fortunately, since we added CompositionCache records in pairs, we // know exactly the set of TransformState objects that have us in their // cache: it's the same set of TransformState objects that we have in // our own cache. // We do need to put considerable thought into this loop, because as // we clear out cache entries we'll cause other TransformState // objects to destruct, which could cause things to get pulled out // of our own _composition_cache map. We want to allow this (so // that we don't encounter any just-destructed pointers in our // cache), but we don't want to get bitten by this cascading effect. // Instead of walking through the map from beginning to end, // therefore, we just pull out the first one each time, and erase // it. // There are lots of ways to do this loop wrong. Be very careful if // you need to modify it for any reason. while (!_composition_cache.empty()) { CompositionCache::iterator ci = _composition_cache.begin(); // It is possible that the "other" TransformState object is // currently within its own destructor. We therefore can't use a // PT() to hold its pointer; that could end up calling its // destructor twice. Fortunately, we don't need to hold its // reference count to ensure it doesn't destruct while we process // this loop; as long as we ensure that no *other* TransformState // objects destruct, there will be no reason for that one to. TransformState *other = (TransformState *)(*ci).first; // We should never have a reflexive entry in this map. If we // do, something got screwed up elsewhere. nassertv(other != this); // We hold a copy of the composition result to ensure that the // result TransformState object (if there is one) doesn't // destruct. Composition comp = (*ci).second; // Now we can remove the element from our cache. We do this now, // rather than later, before any other TransformState objects have // had a chance to destruct, so we are confident that our iterator // is still valid. _composition_cache.erase(ci); CompositionCache::iterator oci = other->_composition_cache.find(this); // We may or may not still be listed in the other's cache (it // might be halfway through pulling entries out, from within its // own destructor). if (oci != other->_composition_cache.end()) { // Hold a copy of the other composition result, too. Composition ocomp = (*oci).second; // Now we're holding a reference count to both computed // results, so no objects will be tempted to destruct while we // erase the other cache entry. other->_composition_cache.erase(oci); } // It's finally safe to let our held pointers go away. This may // have cascading effects as other TransformState objects are // destructed, but there will be no harm done if they destruct // now. } // A similar bit of code for the invert cache. while (!_invert_composition_cache.empty()) { CompositionCache::iterator ci = _invert_composition_cache.begin(); TransformState *other = (TransformState *)(*ci).first; nassertv(other != this); Composition comp = (*ci).second; _invert_composition_cache.erase(ci); CompositionCache::iterator oci = other->_invert_composition_cache.find(this); if (oci != other->_invert_composition_cache.end()) { Composition ocomp = (*oci).second; other->_invert_composition_cache.erase(oci); } } // Also, if we called compose(this) at some point and the return // value was something other than this, we need to decrement the // associated reference count. if (_self_compose != (TransformState *)NULL && _self_compose != this) { unref_delete((TransformState *)_self_compose); } // If this was true at the beginning of the destructor, but is no // longer true now, probably we've been double-deleted. nassertv(get_ref_count() == 0); } //////////////////////////////////////////////////////////////////// // Function: TransformState::operator < // Access: Public // Description: Provides an arbitrary ordering among all unique // TransformStates, so we can store the essentially // different ones in a big set and throw away the rest. //////////////////////////////////////////////////////////////////// bool TransformState:: operator < (const TransformState &other) const { static const int significant_flags = (F_is_invalid | F_is_identity | F_components_given | F_hpr_given); int flags = (_flags & significant_flags); int other_flags = (other._flags & significant_flags); if (flags != other_flags) { return flags < other_flags; } if ((_flags & (F_is_invalid | F_is_identity)) != 0) { // All invalid transforms are equivalent to each other, and all // identity transforms are equivalent to each other. return 0; } if ((_flags & (F_components_given | F_hpr_given | F_quat_given)) == (F_components_given | F_hpr_given | F_quat_given)) { // If the transform was specified componentwise, compare them // componentwise. int c = _pos.compare_to(other._pos); if (c != 0) { return c < 0; } if ((_flags & F_hpr_given) != 0) { c = _hpr.compare_to(other._hpr); if (c != 0) { return c < 0; } } else if ((_flags & F_quat_given) != 0) { c = _quat.compare_to(other._quat); if (c != 0) { return c < 0; } } c = _scale.compare_to(other._scale); if (c != 0) { return c < 0; } c = _shear.compare_to(other._shear); return c < 0; } /* // Otherwise, compare the matrices. return get_mat() < other.get_mat(); */ // On second thought, we don't gain a lot of benefit by going // through all the work of comparing different transforms by matrix. // Doing so ensures that two differently-computed transforms that // happen to encode the same matrix (an unlikely occurrence) will be // collapsed into a single pointer (a tiny benefit). We're better // off not paying the cost of this comparison, and just assuming // that any two differently-computed transforms are essentially // different. return (this < &other); } //////////////////////////////////////////////////////////////////// // Function: TransformState::make_identity // Access: Published, Static // Description: Constructs an identity transform. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: make_identity() { // The identity state is asked for so often, we make it a special case // and store a pointer forever once we find it the first time. if (_identity_state == (TransformState *)NULL) { TransformState *state = new TransformState; _identity_state = return_new(state); } return _identity_state; } //////////////////////////////////////////////////////////////////// // Function: TransformState::make_invalid // Access: Published, Static // Description: Constructs an invalid transform; for instance, the // result of inverting a singular matrix. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: make_invalid() { TransformState *state = new TransformState; state->_flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known; return return_new(state); } //////////////////////////////////////////////////////////////////// // Function: TransformState::make_pos_hpr_scale_shear // Access: Published, Static // Description: Makes a new TransformState with the specified // components. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: make_pos_hpr_scale_shear(const LVecBase3f &pos, const LVecBase3f &hpr, const LVecBase3f &scale, const LVecBase3f &shear) { // Make a special-case check for the identity transform. if (pos == LVecBase3f(0.0f, 0.0f, 0.0f) && hpr == LVecBase3f(0.0f, 0.0f, 0.0f) && scale == LVecBase3f(1.0f, 1.0f, 1.0f) && shear == LVecBase3f(0.0f, 0.0f, 0.0f)) { return make_identity(); } TransformState *state = new TransformState; state->_pos = pos; state->_hpr = hpr; state->_scale = scale; state->_shear = shear; state->_flags = F_components_given | F_hpr_given | F_components_known | F_hpr_known | F_has_components; state->check_uniform_scale(); return return_new(state); } //////////////////////////////////////////////////////////////////// // Function: TransformState::make_pos_quat_scale_shear // Access: Published, Static // Description: Makes a new TransformState with the specified // components. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: make_pos_quat_scale_shear(const LVecBase3f &pos, const LQuaternionf &quat, const LVecBase3f &scale, const LVecBase3f &shear) { // Make a special-case check for the identity transform. if (pos == LVecBase3f(0.0f, 0.0f, 0.0f) && quat == LQuaternionf::ident_quat() && scale == LVecBase3f(1.0f, 1.0f, 1.0f) && shear == LVecBase3f(0.0f, 0.0f, 0.0f)) { return make_identity(); } TransformState *state = new TransformState; state->_pos = pos; state->_quat = quat; state->_scale = scale; state->_shear = shear; state->_flags = F_components_given | F_quat_given | F_components_known | F_quat_known | F_has_components; state->check_uniform_scale(); return return_new(state); } //////////////////////////////////////////////////////////////////// // Function: TransformState::make_mat // Access: Published, Static // Description: Makes a new TransformState with the specified // transformation matrix. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: make_mat(const LMatrix4f &mat) { // Make a special-case check for the identity matrix. if (mat == LMatrix4f::ident_mat()) { return make_identity(); } TransformState *state = new TransformState; state->_mat = mat; state->_flags = F_mat_known; return return_new(state); } //////////////////////////////////////////////////////////////////// // Function: TransformState::set_pos // Access: Published // Description: Returns a new TransformState object that represents the // original TransformState with its pos component // replaced with the indicated value. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: set_pos(const LVecBase3f &pos) const { nassertr(!is_invalid(), this); if (is_identity() || components_given()) { // If we started with a componentwise transform, we keep it that // way. if (quat_given()) { return make_pos_quat_scale_shear(pos, get_quat(), get_scale(), get_shear()); } else { return make_pos_hpr_scale_shear(pos, get_hpr(), get_scale(), get_shear()); } } else { // Otherwise, we have a matrix transform, and we keep it that way. LMatrix4f mat = get_mat(); mat.set_row(3, pos); return make_mat(mat); } } //////////////////////////////////////////////////////////////////// // Function: TransformState::set_hpr // Access: Published // Description: Returns a new TransformState object that represents the // original TransformState with its rotation component // replaced with the indicated value, if possible. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: set_hpr(const LVecBase3f &hpr) const { nassertr(!is_invalid(), this); // nassertr(has_components(), this); return make_pos_hpr_scale_shear(get_pos(), hpr, get_scale(), get_shear()); } //////////////////////////////////////////////////////////////////// // Function: TransformState::set_quat // Access: Published // Description: Returns a new TransformState object that represents the // original TransformState with its rotation component // replaced with the indicated value, if possible. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: set_quat(const LQuaternionf &quat) const { nassertr(!is_invalid(), this); // nassertr(has_components(), this); return make_pos_quat_scale_shear(get_pos(), quat, get_scale(), get_shear()); } //////////////////////////////////////////////////////////////////// // Function: TransformState::set_scale // Access: Published // Description: Returns a new TransformState object that represents the // original TransformState with its scale component // replaced with the indicated value, if possible. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: set_scale(const LVecBase3f &scale) const { nassertr(!is_invalid(), this); // nassertr(has_components(), this); if (quat_given()) { return make_pos_quat_scale_shear(get_pos(), get_quat(), scale, get_shear()); } else { return make_pos_hpr_scale_shear(get_pos(), get_hpr(), scale, get_shear()); } } //////////////////////////////////////////////////////////////////// // Function: TransformState::set_shear // Access: Published // Description: Returns a new TransformState object that represents the // original TransformState with its shear component // replaced with the indicated value, if possible. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: set_shear(const LVecBase3f &shear) const { nassertr(!is_invalid(), this); // nassertr(has_components(), this); if (quat_given()) { return make_pos_quat_scale_shear(get_pos(), get_quat(), get_scale(), shear); } else { return make_pos_hpr_scale_shear(get_pos(), get_hpr(), get_scale(), shear); } } //////////////////////////////////////////////////////////////////// // Function: TransformState::compose // Access: Published // Description: Returns a new TransformState object that represents the // composition of this state with the other state. // // The result of this operation is cached, and will be // retained as long as both this TransformState object and // the other TransformState object continue to exist. // Should one of them destruct, the cached entry will be // removed, and its pointer will be allowed to destruct // as well. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: compose(const TransformState *other) const { // This method isn't strictly const, because it updates the cache, // but we pretend that it is because it's only a cache which is // transparent to the rest of the interface. // We handle identity as a trivial special case. if (is_identity()) { return other; } if (other->is_identity()) { return this; } // If either transform is invalid, the result is invalid. if (is_invalid()) { return this; } if (other->is_invalid()) { return other; } if (other == this) { // compose(this) has to be handled as a special case, because the // caching problem is so different. if (_self_compose != (TransformState *)NULL) { return _self_compose; } CPT(TransformState) result = do_compose(this); ((TransformState *)this)->_self_compose = result; if (result != (const TransformState *)this) { // If the result of compose(this) is something other than this, // explicitly increment the reference count. We have to be sure // to decrement it again later, in our destructor. _self_compose->ref(); // (If the result was just this again, we still store the // result, but we don't increment the reference count, since // that would be a self-referential leak. What a mess this is.) } return _self_compose; } // Is this composition already cached? CompositionCache::const_iterator ci = _composition_cache.find(other); if (ci != _composition_cache.end()) { const Composition &comp = (*ci).second; if (comp._result == (const TransformState *)NULL) { // Well, it wasn't cached already, but we already had an entry // (probably created for the reverse direction), so use the same // entry to store the new result. ((Composition &)comp)._result = do_compose(other); } // Here's the cache! return comp._result; } // We need to make a new cache entry, both in this object and in the // other object. We make both records so the other TransformState // object will know to delete the entry from this object when it // destructs, and vice-versa. // The cache entry in this object is the only one that indicates the // result; the other will be NULL for now. CPT(TransformState) result = do_compose(other); ((TransformState *)other)->_composition_cache[this]._result = NULL; ((TransformState *)this)->_composition_cache[other]._result = result; return result; } //////////////////////////////////////////////////////////////////// // Function: TransformState::invert_compose // Access: Published // Description: Returns a new TransformState object that represents the // composition of this state's inverse with the other // state. // // This is similar to compose(), but is particularly // useful for computing the relative state of a node as // viewed from some other node. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: invert_compose(const TransformState *other) const { // This method isn't strictly const, because it updates the cache, // but we pretend that it is because it's only a cache which is // transparent to the rest of the interface. // We handle identity as a trivial special case. if (is_identity()) { return other; } // Unlike compose(), the case of other->is_identity() is not quite as // trivial for invert_compose(). // If either transform is invalid, the result is invalid. if (is_invalid()) { return this; } if (other->is_invalid()) { return other; } if (other == this) { // a->invert_compose(a) always produces identity. return make_identity(); } // Is this composition already cached? CompositionCache::const_iterator ci = _invert_composition_cache.find(other); if (ci != _invert_composition_cache.end()) { const Composition &comp = (*ci).second; if (comp._result == (const TransformState *)NULL) { // Well, it wasn't cached already, but we already had an entry // (probably created for the reverse direction), so use the same // entry to store the new result. ((Composition &)comp)._result = do_invert_compose(other); } // Here's the cache! return comp._result; } // We need to make a new cache entry, both in this object and in the // other object. We make both records so the other TransformState // object will know to delete the entry from this object when it // destructs, and vice-versa. // The cache entry in this object is the only one that indicates the // result; the other will be NULL for now. CPT(TransformState) result = do_invert_compose(other); ((TransformState *)other)->_invert_composition_cache[this]._result = NULL; ((TransformState *)this)->_invert_composition_cache[other]._result = result; return result; } //////////////////////////////////////////////////////////////////// // Function: TransformState::output // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// void TransformState:: output(ostream &out) const { out << "T:"; if (is_invalid()) { out << "(invalid)"; } else if (is_identity()) { out << "(identity)"; } else if (has_components()) { bool output_hpr = !get_hpr().almost_equal(LVecBase3f(0.0f, 0.0f, 0.0f)); if (!components_given()) { // A leading "m" indicates the transform was described as a full // matrix, and we are decomposing it for the benefit of the // user. out << "m"; } else if (output_hpr && quat_given()) { // A leading "q" indicates that the pos, scale, and shear are // exactly as specified, but the rotation was described as a // quaternion, and we are decomposing that to hpr for the // benefit of the user. out << "q"; } char lead = '('; if (!get_pos().almost_equal(LVecBase3f(0.0f, 0.0f, 0.0f))) { out << lead << "pos " << get_pos(); lead = ' '; } if (output_hpr) { out << lead << "hpr " << get_hpr(); lead = ' '; } if (!get_scale().almost_equal(LVecBase3f(1.0f, 1.0f, 1.0f))) { if (has_uniform_scale()) { out << lead << "scale " << get_uniform_scale(); lead = ' '; } else { out << lead << "scale " << get_scale(); lead = ' '; } } if (has_nonzero_shear()) { out << lead << "shear " << get_shear(); lead = ' '; } if (lead == '(') { out << "(almost identity)"; } else { out << ")"; } } else { out << get_mat(); } } //////////////////////////////////////////////////////////////////// // Function: TransformState::write // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// void TransformState:: write(ostream &out, int indent_level) const { indent(out, indent_level) << *this << "\n"; } //////////////////////////////////////////////////////////////////// // Function: TransformState::get_num_states // Access: Published, Static // Description: Returns the total number of unique TransformState // objects allocated in the world. This will go up and // down during normal operations. //////////////////////////////////////////////////////////////////// int TransformState:: get_num_states() { if (_states == (States *)NULL) { return 0; } return _states->size(); } //////////////////////////////////////////////////////////////////// // Function: TransformState::get_num_unused_states // Access: Published, Static // Description: Returns the total number of TransformState objects // that have been allocated but have no references // outside of the internal TransformState map. //////////////////////////////////////////////////////////////////// int TransformState:: get_num_unused_states() { if (_states == (States *)NULL) { return 0; } int num_unused = 0; // First, we need to count the number of times each TransformState // object is recorded in the cache. typedef pmap StateCount; StateCount state_count; States::iterator si; for (si = _states->begin(); si != _states->end(); ++si) { const TransformState *state = (*si); CompositionCache::const_iterator ci; for (ci = state->_composition_cache.begin(); ci != state->_composition_cache.end(); ++ci) { const TransformState *result = (*ci).second._result; if (result != (const TransformState *)NULL) { // Here's a TransformState that's recorded in the cache. // Count it. pair ir = state_count.insert(StateCount::value_type(result, 1)); if (!ir.second) { // If the above insert operation fails, then it's already in // the cache; increment its value. (*(ir.first)).second++; } } } for (ci = state->_invert_composition_cache.begin(); ci != state->_invert_composition_cache.end(); ++ci) { const TransformState *result = (*ci).second._result; if (result != (const TransformState *)NULL) { pair ir = state_count.insert(StateCount::value_type(result, 1)); if (!ir.second) { (*(ir.first)).second++; } } } // Finally, check the self_compose field, which might be reference // counted too. if (state->_self_compose != (const TransformState *)NULL && state->_self_compose != state) { const TransformState *result = state->_self_compose; if (result != (const TransformState *)NULL) { pair ir = state_count.insert(StateCount::value_type(result, 1)); if (!ir.second) { (*(ir.first)).second++; } } } } // Now that we have the appearance count of each TransformState // object, we can tell which ones are unreferenced outside of the // TransformState cache, by comparing these to the reference counts. StateCount::iterator sci; for (sci = state_count.begin(); sci != state_count.end(); ++sci) { const TransformState *state = (*sci).first; int count = (*sci).second; nassertr(count <= state->get_ref_count(), num_unused); if (count == state->get_ref_count()) { num_unused++; } } return num_unused; } //////////////////////////////////////////////////////////////////// // Function: TransformState::clear_cache // Access: Published, Static // Description: Empties the cache of composed TransformStates. This // makes every TransformState forget what results when // it is composed with other TransformStates. // // This will eliminate any TransformState objects that // have been allocated but have no references outside of // the internal TransformState map. It will not // eliminate TransformState objects that are still in // use. // // Normally, TransformState objects will remove // themselves from the interal map when their reference // counts go to 0, but since circular references are // possible there may be some cycles that cannot remove // themselves. Calling this function from time to time // will ensure there is no wasteful memory leakage, but // calling it too often may result in decreased // performance as the cache is forced to be recomputed. // // The return value is the number of TransformStates // freed by this operation. //////////////////////////////////////////////////////////////////// int TransformState:: clear_cache() { if (_states == (States *)NULL) { return 0; } int orig_size = _states->size(); // First, we need to copy the entire set of transforms to a // temporary vector, reference-counting each object. That way we // can walk through the copy, without fear of dereferencing (and // deleting) the objects in the map as we go. { typedef pvector< CPT(TransformState) > TempStates; TempStates temp_states; temp_states.reserve(orig_size); copy(_states->begin(), _states->end(), back_inserter(temp_states)); // Now it's safe to walk through the list, destroying the cache // within each object as we go. Nothing will be destructed till // we're done. TempStates::iterator ti; for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) { TransformState *state = (TransformState *)(*ti).p(); state->_composition_cache.clear(); state->_invert_composition_cache.clear(); if (state->_self_compose != (TransformState *)NULL && state->_self_compose != state) { unref_delete((TransformState *)state->_self_compose); state->_self_compose = (TransformState *)NULL; } } // Once this block closes and the temp_states object goes away, // all the destruction will begin. Anything whose reference was // held only within the various objects' caches will go away. } int new_size = _states->size(); return orig_size - new_size; } //////////////////////////////////////////////////////////////////// // Function: TransformState::return_new // Access: Private, Static // Description: This function is used to share a common TransformState // pointer for all equivalent TransformState objects. // // See the similar logic in RenderState. The idea is to // create a new TransformState object and pass it // through this function, which will share the pointer // with a previously-created TransformState object if it // is equivalent. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: return_new(TransformState *state) { nassertr(state != (TransformState *)NULL, state); // This should be a newly allocated pointer, not one that was used // for anything else. nassertr(state->_saved_entry == _states->end(), state); // Save the state in a local PointerTo so that it will be freed at // the end of this function if no one else uses it. CPT(TransformState) pt_state = state; pair result = _states->insert(state); if (result.second) { // The state was inserted; save the iterator and return the // input state. state->_saved_entry = result.first; return pt_state; } // The state was not inserted; there must be an equivalent one // already in the set. Return that one. return *(result.first); } //////////////////////////////////////////////////////////////////// // Function: TransformState::do_compose // Access: Private // Description: The private implemention of compose(); this actually // composes two TransformStates, without bothering with the // cache. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: do_compose(const TransformState *other) const { nassertr((_flags & F_is_invalid) == 0, this); nassertr((other->_flags & F_is_invalid) == 0, other); if (compose_componentwise && has_uniform_scale() && !has_nonzero_shear() && !other->has_nonzero_shear() && ((components_given() && other->has_components()) || (other->components_given() && has_components()))) { // We will do this operation componentwise if *either* transform // was given componentwise (and there is no non-uniform scale in // the way). LVecBase3f pos = get_pos(); LQuaternionf quat = get_quat(); float scale = get_uniform_scale(); pos += quat.xform(other->get_pos()) * scale; quat = other->get_quat() * quat; quat.normalize(); LVecBase3f new_scale = other->get_scale() * scale; CPT(TransformState) result = make_pos_quat_scale(pos, quat, new_scale); #ifndef NDEBUG if (paranoid_compose) { // Now verify against the matrix. LMatrix4f new_mat = other->get_mat() * get_mat(); if (!new_mat.almost_equal(result->get_mat(), 0.1)) { CPT(TransformState) correct = make_mat(new_mat); pgraph_cat.warning() << "Componentwise composition of " << *this << " and " << *other << " produced:\n" << *result << "\n instead of:\n" << *correct << "\n"; result = correct; } } #endif // NDEBUG return result; } // Do the operation with matrices. LMatrix4f new_mat = other->get_mat() * get_mat(); return make_mat(new_mat); } //////////////////////////////////////////////////////////////////// // Function: TransformState::do_invert_compose // Access: Private // Description: The private implemention of invert_compose(). //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: do_invert_compose(const TransformState *other) const { nassertr((_flags & F_is_invalid) == 0, this); nassertr((other->_flags & F_is_invalid) == 0, other); if (compose_componentwise && has_uniform_scale() && !has_nonzero_shear() && !other->has_nonzero_shear() && ((components_given() && other->has_components()) || (other->components_given() && has_components()))) { // We will do this operation componentwise if *either* transform // was given componentwise (and there is no non-uniform scale in // the way). LVecBase3f pos = get_pos(); LQuaternionf quat = get_quat(); float scale = get_uniform_scale(); // First, invert our own transform. if (scale == 0.0f) { ((TransformState *)this)->_flags |= F_is_singular | F_singular_known; return make_invalid(); } scale = 1.0f / scale; quat.invert_in_place(); pos = quat.xform(-pos) * scale; LVecBase3f new_scale(scale, scale, scale); // Now compose the inverted transform with the other transform. if (!other->is_identity()) { pos += quat.xform(other->get_pos()) * scale; quat = other->get_quat() * quat; quat.normalize(); new_scale = other->get_scale() * scale; } CPT(TransformState) result = make_pos_quat_scale(pos, quat, new_scale); #ifndef NDEBUG if (paranoid_compose) { // Now verify against the matrix. if (is_singular()) { pgraph_cat.warning() << "Unexpected singular matrix found for " << *this << "\n"; } else { nassertr(_inv_mat != (LMatrix4f *)NULL, make_invalid()); LMatrix4f new_mat = other->get_mat() * (*_inv_mat); if (!new_mat.almost_equal(result->get_mat(), 0.1)) { CPT(TransformState) correct = make_mat(new_mat); pgraph_cat.warning() << "Componentwise invert-composition of " << *this << " and " << *other << " produced:\n" << *result << "\n instead of:\n" << *correct << "\n"; result = correct; } } } #endif // NDEBUG return result; } if (is_singular()) { return make_invalid(); } // Now that is_singular() has returned false, we can assume that // _inv_mat has been allocated and filled in. nassertr(_inv_mat != (LMatrix4f *)NULL, make_invalid()); if (other->is_identity()) { return make_mat(*_inv_mat); } else { return make_mat(other->get_mat() * (*_inv_mat)); } } //////////////////////////////////////////////////////////////////// // Function: TransformState::calc_singular // Access: Private // Description: Determines whether the transform is singular (i.e. it // scales to zero, and has no inverse). //////////////////////////////////////////////////////////////////// void TransformState:: calc_singular() { nassertv((_flags & F_is_invalid) == 0); // We determine if a matrix is singular by attempting to invert it // (and we save the result of this invert operation for a subsequent // do_invert_compose() call, which is almost certain to be made if // someone is asking whether we're singular). // This should be NULL if no one has called calc_singular() yet. nassertv(_inv_mat == (LMatrix4f *)NULL); _inv_mat = new LMatrix4f; bool inverted = _inv_mat->invert_from(get_mat()); if (!inverted) { _flags |= F_is_singular; delete _inv_mat; _inv_mat = (LMatrix4f *)NULL; } _flags |= F_singular_known; } //////////////////////////////////////////////////////////////////// // Function: TransformState::calc_components // Access: Private // Description: Derives the components from the matrix, if possible. //////////////////////////////////////////////////////////////////// void TransformState:: calc_components() { nassertv((_flags & F_is_invalid) == 0); if ((_flags & F_is_identity) != 0) { _scale.set(1.0f, 1.0f, 1.0f); _shear.set(0.0f, 0.0f, 0.0f); _hpr.set(0.0f, 0.0f, 0.0f); _quat = LQuaternionf::ident_quat(); _pos.set(0.0f, 0.0f, 0.0f); _flags |= F_has_components | F_components_known | F_hpr_known | F_quat_known | F_uniform_scale; } else { // If we don't have components and we're not identity, the only // other explanation is that we were constructed via a matrix. nassertv((_flags & F_mat_known) != 0); const LMatrix4f &mat = get_mat(); bool possible = decompose_matrix(mat, _scale, _shear, _hpr, _pos); if (!possible) { // Some matrices can't be decomposed into scale, hpr, pos. In // this case, we now know that we cannot compute the components; // but the closest approximations are stored, at least. _flags |= F_components_known | F_hpr_known; } else { // Otherwise, we do have the components, or at least the hpr. _flags |= F_has_components | F_components_known | F_hpr_known; check_uniform_scale(); } // However, we can always get at least the pos. mat.get_row3(_pos, 3); } } //////////////////////////////////////////////////////////////////// // Function: TransformState::calc_hpr // Access: Private // Description: Derives the hpr, from the matrix if necessary, or // from the quat. //////////////////////////////////////////////////////////////////// void TransformState:: calc_hpr() { nassertv((_flags & F_is_invalid) == 0); check_components(); if ((_flags & F_hpr_known) == 0) { // If we don't know the hpr yet, we must have been given a quat. // Decompose it. nassertv((_flags & F_quat_known) != 0); _hpr = _quat.get_hpr(); _flags |= F_hpr_known; } } //////////////////////////////////////////////////////////////////// // Function: TransformState::calc_quat // Access: Private // Description: Derives the quat from the hpr. //////////////////////////////////////////////////////////////////// void TransformState:: calc_quat() { nassertv((_flags & F_is_invalid) == 0); check_components(); if ((_flags & F_quat_known) == 0) { // If we don't know the quat yet, we must have been given a hpr. // Decompose it. nassertv((_flags & F_hpr_known) != 0); _quat.set_hpr(_hpr); _flags |= F_quat_known; } } //////////////////////////////////////////////////////////////////// // Function: TransformState::calc_mat // Access: Private // Description: Computes the matrix from the components. //////////////////////////////////////////////////////////////////// void TransformState:: calc_mat() { nassertv((_flags & F_is_invalid) == 0); if ((_flags & F_is_identity) != 0) { _mat = LMatrix4f::ident_mat(); } else { // If we don't have a matrix and we're not identity, the only // other explanation is that we were constructed via components. nassertv((_flags & F_components_known) != 0); compose_matrix(_mat, _scale, _shear, get_hpr(), _pos); } _flags |= F_mat_known; } //////////////////////////////////////////////////////////////////// // Function: TransformState::register_with_read_factory // Access: Public, Static // Description: Tells the BamReader how to create objects of type // TransformState. //////////////////////////////////////////////////////////////////// void TransformState:: register_with_read_factory() { BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); } //////////////////////////////////////////////////////////////////// // Function: TransformState::write_datagram // Access: Public, Virtual // Description: Writes the contents of this object to the datagram // for shipping out to a Bam file. //////////////////////////////////////////////////////////////////// void TransformState:: write_datagram(BamWriter *manager, Datagram &dg) { TypedWritable::write_datagram(manager, dg); if ((_flags & F_is_identity) != 0) { // Identity, nothing much to that. int flags = F_is_identity | F_singular_known; dg.add_uint16(flags); } else if ((_flags & F_is_invalid) != 0) { // Invalid, nothing much to that either. int flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known; dg.add_uint16(flags); } else if ((_flags & F_components_given) != 0) { // A component-based transform. int flags = F_components_given | F_components_known | F_has_components; if ((_flags & F_quat_given) != 0) { flags |= (F_quat_given | F_quat_known); } else if ((_flags & F_hpr_given) != 0) { flags |= (F_hpr_given | F_hpr_known); } dg.add_uint16(flags); _pos.write_datagram(dg); if ((_flags & F_quat_given) != 0) { _quat.write_datagram(dg); } else { get_hpr().write_datagram(dg); } _scale.write_datagram(dg); _shear.write_datagram(dg); } else { // A general matrix. nassertv((_flags & F_mat_known) != 0); int flags = F_mat_known; dg.add_uint16(flags); _mat.write_datagram(dg); } } //////////////////////////////////////////////////////////////////// // Function: TransformState::change_this // Access: Public, Static // Description: Called immediately after complete_pointers(), this // gives the object a chance to adjust its own pointer // if desired. Most objects don't change pointers after // completion, but some need to. // // Once this function has been called, the old pointer // will no longer be accessed. //////////////////////////////////////////////////////////////////// TypedWritable *TransformState:: change_this(TypedWritable *old_ptr, BamReader *manager) { // First, uniquify the pointer. TransformState *state = DCAST(TransformState, old_ptr); CPT(TransformState) pointer = return_new(state); // But now we have a problem, since we have to hold the reference // count and there's no way to return a TypedWritable while still // holding the reference count! We work around this by explicitly // upping the count, and also setting a finalize() callback to down // it later. if (pointer == state) { pointer->ref(); manager->register_finalize(state); } // We have to cast the pointer back to non-const, because the bam // reader expects that. return (TransformState *)pointer.p(); } //////////////////////////////////////////////////////////////////// // Function: TransformState::finalize // Access: Public, Virtual // Description: Called by the BamReader to perform any final actions // needed for setting up the object after all objects // have been read and all pointers have been completed. //////////////////////////////////////////////////////////////////// void TransformState:: finalize() { // Unref the pointer that we explicitly reffed in make_from_bam(). unref(); // We should never get back to zero after unreffing our own count, // because we expect to have been stored in a pointer somewhere. If // we do get to zero, it's a memory leak; the way to avoid this is // to call unref_delete() above instead of unref(), but this is // dangerous to do from within a virtual function. nassertv(get_ref_count() != 0); } //////////////////////////////////////////////////////////////////// // Function: TransformState::make_from_bam // Access: Protected, Static // Description: This function is called by the BamReader's factory // when a new object of type TransformState is encountered // in the Bam file. It should create the TransformState // and extract its information from the file. //////////////////////////////////////////////////////////////////// TypedWritable *TransformState:: make_from_bam(const FactoryParams ¶ms) { TransformState *state = new TransformState; DatagramIterator scan; BamReader *manager; parse_params(params, scan, manager); state->fillin(scan, manager); manager->register_change_this(change_this, state); return state; } //////////////////////////////////////////////////////////////////// // Function: TransformState::fillin // Access: Protected // Description: This internal function is called by make_from_bam to // read in all of the relevant data from the BamFile for // the new TransformState. //////////////////////////////////////////////////////////////////// void TransformState:: fillin(DatagramIterator &scan, BamReader *manager) { TypedWritable::fillin(scan, manager); _flags = scan.get_uint16(); if ((_flags & F_components_given) != 0) { // Componentwise transform. _pos.read_datagram(scan); if ((_flags & F_quat_given) != 0) { _quat.read_datagram(scan); } else { _hpr.read_datagram(scan); // Holdover support for bams 4.0 or older: add these bits that // should have been added when the bam was written. _flags |= (F_hpr_given | F_hpr_known); } _scale.read_datagram(scan); if (manager->get_file_minor_ver() >= 6) { _shear.read_datagram(scan); } else { _shear.set(0.0f, 0.0f, 0.0f); } check_uniform_scale(); } if ((_flags & F_mat_known) != 0) { // General matrix. _mat.read_datagram(scan); } }