|
|
@@ -0,0 +1,650 @@
|
|
|
+// 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
|
|
|
+// [email protected] .
|
|
|
+//
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+#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;
|
|
|
+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() {
|
|
|
+ _saved_entry = _states.end();
|
|
|
+ _self_compose = (TransformState *)NULL;
|
|
|
+ _flags = F_is_identity | F_singular_known;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// 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() {
|
|
|
+ // Remove the deleted TransformState object from the global pool.
|
|
|
+ if (_saved_entry != _states.end()) {
|
|
|
+ _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 some 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 don't want to get bitten by this
|
|
|
+ // cascading effect.
|
|
|
+ CompositionCache::iterator ci;
|
|
|
+ ci = _composition_cache.begin();
|
|
|
+ while (ci != _composition_cache.end()) {
|
|
|
+ {
|
|
|
+ PT(TransformState) other = (TransformState *)(*ci).first;
|
|
|
+ Composition comp = (*ci).second;
|
|
|
+
|
|
|
+ // We should never have a reflexive entry in this map. If we
|
|
|
+ // do, something got screwed up elsewhere.
|
|
|
+ nassertv(other != (const TransformState *)this);
|
|
|
+
|
|
|
+ // Now we're holding a reference count to the other state, as well
|
|
|
+ // as to the computed result (if any), so neither object will be
|
|
|
+ // tempted to destruct. Go ahead and remove ourselves from the
|
|
|
+ // other cache.
|
|
|
+ other->_composition_cache.erase(this);
|
|
|
+
|
|
|
+ // It's all right if the other state destructs now, since it
|
|
|
+ // won't try to remove itself from our own composition cache any
|
|
|
+ // more. Someone might conceivably delete the *next* entry,
|
|
|
+ // though, so we should be sure to let all that deleting finish
|
|
|
+ // up before we attempt to increment ci, by closing the scope
|
|
|
+ // here.
|
|
|
+ }
|
|
|
+ // Now it's safe to increment ci, because the current cache entry
|
|
|
+ // has not gone away, and if the next one has, by now it's safely
|
|
|
+ // gone.
|
|
|
+ ++ci;
|
|
|
+ }
|
|
|
+
|
|
|
+ // A similar bit of code for the invert cache.
|
|
|
+ ci = _invert_composition_cache.begin();
|
|
|
+ while (ci != _invert_composition_cache.end()) {
|
|
|
+ {
|
|
|
+ PT(TransformState) other = (TransformState *)(*ci).first;
|
|
|
+ Composition comp = (*ci).second;
|
|
|
+ nassertv(other != (const TransformState *)this);
|
|
|
+ other->_invert_composition_cache.erase(this);
|
|
|
+ }
|
|
|
+ ++ci;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// 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.
|
|
|
+//
|
|
|
+// This method is not needed outside of the TransformState
|
|
|
+// class because all equivalent TransformState objects are
|
|
|
+// guaranteed to share the same pointer; thus, a pointer
|
|
|
+// comparison is always sufficient.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+bool TransformState::
|
|
|
+operator < (const TransformState &other) const {
|
|
|
+ bool components_given = (_flags & F_components_given) != 0;
|
|
|
+ bool other_components_given = (other._flags & F_components_given) != 0;
|
|
|
+ if (components_given != other_components_given) {
|
|
|
+ return components_given < other_components_given;
|
|
|
+ }
|
|
|
+ if (components_given) {
|
|
|
+ // If the transform was specified componentwise, compare them
|
|
|
+ // componentwise.
|
|
|
+ int c = _pos.compare_to(other._pos);
|
|
|
+ if (c != 0) {
|
|
|
+ return c < 0;
|
|
|
+ }
|
|
|
+ c = _hpr.compare_to(other._hpr);
|
|
|
+ if (c != 0) {
|
|
|
+ return c < 0;
|
|
|
+ }
|
|
|
+ c = _scale.compare_to(other._hpr);
|
|
|
+ return c < 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Otherwise, compare the matrices.
|
|
|
+ return get_mat() < other.get_mat();
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// 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_pos_hpr_scale
|
|
|
+// Access: Published, Static
|
|
|
+// Description: Makes a new TransformState with the specified
|
|
|
+// components.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+CPT(TransformState) TransformState::
|
|
|
+make_pos_hpr_scale(const LVecBase3f &pos, const LVecBase3f &hpr,
|
|
|
+ const LVecBase3f &scale) {
|
|
|
+ // 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)) {
|
|
|
+ return make_identity();
|
|
|
+ }
|
|
|
+
|
|
|
+ TransformState *attrib = new TransformState;
|
|
|
+ attrib->_pos = pos;
|
|
|
+ attrib->_hpr = hpr;
|
|
|
+ attrib->_scale = scale;
|
|
|
+ attrib->_flags = F_components_given | F_components_known | F_has_components;
|
|
|
+ return return_new(attrib);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// 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 *attrib = new TransformState;
|
|
|
+ attrib->_mat = mat;
|
|
|
+ attrib->_flags = F_mat_known;
|
|
|
+ return return_new(attrib);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// 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 (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);
|
|
|
+ // We store them in this order, on the off-chance that other is the
|
|
|
+ // same as this, a degenerate case which is still worth supporting.
|
|
|
+ ((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 (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);
|
|
|
+ // We store them in this order, on the off-chance that other is the
|
|
|
+ // same as this, a degenerate case which is still worth supporting.
|
|
|
+ ((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 << get_type() << ":";
|
|
|
+ if (is_identity()) {
|
|
|
+ out << "(identity)";
|
|
|
+
|
|
|
+ } else if (has_components()) {
|
|
|
+ out << "(";
|
|
|
+ if (get_pos() != LVecBase3f(0.0f, 0.0f, 0.0f)) {
|
|
|
+ out << "pos " << get_pos();
|
|
|
+ }
|
|
|
+ if (get_hpr() != LVecBase3f(0.0f, 0.0f, 0.0f)) {
|
|
|
+ out << "hpr " << get_hpr();
|
|
|
+ }
|
|
|
+ if (get_scale() != LVecBase3f(1.0f, 1.0f, 1.0f)) {
|
|
|
+ out << "scale " << get_scale();
|
|
|
+ }
|
|
|
+ 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::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 RenderAttrib. 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<States::iterator, bool> 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 {
|
|
|
+ LMatrix4f new_mat = get_mat() * other->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 {
|
|
|
+ LMatrix4f new_mat;
|
|
|
+ new_mat.invert_from(get_mat());
|
|
|
+ new_mat = new_mat * other->get_mat();
|
|
|
+ return make_mat(new_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() {
|
|
|
+ bool singular = false;
|
|
|
+
|
|
|
+ if (has_components()) {
|
|
|
+ // The matrix is singular if any component of its scale is 0.
|
|
|
+ singular = (_scale[0] == 0.0f || _scale[1] == 0.0f || _scale[2] == 0.0f);
|
|
|
+ } else {
|
|
|
+ // The matrix is singular if its determinant is zero.
|
|
|
+ const LMatrix4f &mat = get_mat();
|
|
|
+ singular = (mat.get_upper_3().determinant() == 0.0f);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (singular) {
|
|
|
+ _flags |= F_is_singular;
|
|
|
+ }
|
|
|
+ _flags |= F_singular_known;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: TransformState::calc_components
|
|
|
+// Access: Private
|
|
|
+// Description: Derives the components from the matrix, if possible.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void TransformState::
|
|
|
+calc_components() {
|
|
|
+ if ((_flags & F_is_identity) != 0) {
|
|
|
+ _scale.set(1.0f, 1.0f, 1.0f);
|
|
|
+ _hpr.set(0.0f, 0.0f, 0.0f);
|
|
|
+ _pos.set(0.0f, 0.0f, 0.0f);
|
|
|
+ _flags |= F_has_components;
|
|
|
+
|
|
|
+ } 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);
|
|
|
+
|
|
|
+ bool possible = decompose_matrix(get_mat(), _scale, _hpr, _pos);
|
|
|
+ if (possible) {
|
|
|
+ // Some matrices can't be decomposed into scale, hpr, pos.
|
|
|
+ _flags |= F_has_components;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _flags |= F_components_known;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: TransformState::calc_mat
|
|
|
+// Access: Private
|
|
|
+// Description: Computes the matrix from the components.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void TransformState::
|
|
|
+calc_mat() {
|
|
|
+ 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, _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) {
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: TransformState::finalize
|
|
|
+// Access: Public, Virtual
|
|
|
+// Description: Method to ensure that any necessary clean up tasks
|
|
|
+// that have to be performed by this object are performed
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+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);
|
|
|
+
|
|
|
+ return new_from_bam(state, manager);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: TransformState::new_from_bam
|
|
|
+// Access: Protected, Static
|
|
|
+// Description: Uniquifies the pointer for a TransformState object just
|
|
|
+// created from a bam file, and preserves its reference
|
|
|
+// count correctly.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+TypedWritable *TransformState::
|
|
|
+new_from_bam(TransformState *state, BamReader *manager) {
|
|
|
+ // First, uniquify the pointer.
|
|
|
+ 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::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) {
|
|
|
+}
|