From bc946860e62ba1c1e67ea91a280bc8c36cab8741 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 20 Jun 2002 00:12:41 +0000 Subject: [PATCH] add get_quat() and get_uniform_scale() --- panda/src/pgraph/transformState.I | 156 ++++++++++++++++++++++++-- panda/src/pgraph/transformState.cxx | 168 ++++++++++++++++++++++++---- panda/src/pgraph/transformState.h | 29 ++++- 3 files changed, 319 insertions(+), 34 deletions(-) diff --git a/panda/src/pgraph/transformState.I b/panda/src/pgraph/transformState.I index f5c9bb3f15..8faa449a6b 100644 --- a/panda/src/pgraph/transformState.I +++ b/panda/src/pgraph/transformState.I @@ -63,6 +63,19 @@ make_hpr(const LVecBase3f &hpr) { LVecBase3f(1.0f, 1.0f, 1.0f)); } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::make_quat +// Access: Published, Static +// Description: Makes a new TransformState with the specified +// components. +//////////////////////////////////////////////////////////////////// +INLINE CPT(TransformState) TransformState:: +make_quat(const LQuaternionf &quat) { + return make_pos_quat_scale(LVecBase3f(0.0f, 0.0f, 0.0f), + quat, + LVecBase3f(1.0f, 1.0f, 1.0f)); +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::make_pos_hpr // Access: Published, Static @@ -168,15 +181,44 @@ has_components() const { // Description: Returns true if the transform was specified // componentwise, or false if it was specified with a // general 4x4 matrix. If this is true, the components -// returned by get_pos(), get_hpr(), and get_scale() -// will be exactly those that were set; otherwise, these -// functions will return computed values. +// returned by get_pos() and get_scale() will be exactly +// those that were set; otherwise, these functions will +// return computed values. If this is true, the +// rotation may have been set either with a hpr trio or +// with a quaternion; hpr_given() or quat_given() can +// resolve the difference. //////////////////////////////////////////////////////////////////// INLINE bool TransformState:: components_given() const { return ((_flags & F_components_given) != 0); } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::hpr_given +// Access: Published +// Description: Returns true if the rotation was specified via a trio +// of Euler angles, false otherwise. If this is true, +// get_hpr() will be exactly as set; otherwise, it will +// return a computed value. +//////////////////////////////////////////////////////////////////// +INLINE bool TransformState:: +hpr_given() const { + return ((_flags & F_hpr_given) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::quat_given +// Access: Published +// Description: Returns true if the rotation was specified via a +// quaternion, false otherwise. If this is true, +// get_quat() will be exactly as set; otherwise, it will +// return a computed value. +//////////////////////////////////////////////////////////////////// +INLINE bool TransformState:: +quat_given() const { + return ((_flags & F_quat_given) != 0); +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::has_pos // Access: Published @@ -203,6 +245,19 @@ has_hpr() const { return has_components(); } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::has_quat +// Access: Published +// Description: Returns true if the transform's rotation component +// can be extracted out separately and described as a +// quaternion. This is generally true only when +// has_components() is true. +//////////////////////////////////////////////////////////////////// +INLINE bool TransformState:: +has_quat() const { + return has_components(); +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::has_scale // Access: Published @@ -215,6 +270,20 @@ has_scale() const { return has_components(); } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::has_uniform_scale +// Access: Published +// Description: Returns true if the scale is uniform across all three +// axes (and therefore can be expressed as a single +// number), or false if the transform has a different +// scale in different dimensions. +//////////////////////////////////////////////////////////////////// +INLINE bool TransformState:: +has_uniform_scale() const { + check_components(); + return (_flags & F_uniform_scale) != 0; +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::has_mat // Access: Published @@ -243,17 +312,31 @@ get_pos() const { //////////////////////////////////////////////////////////////////// // Function: TransformState::get_hpr // Access: Published -// Description: Returns the hpr component of the transform. It is an -// error to call this if has_components() returned -// false. +// Description: Returns the rotation component of the transform as a +// trio of Euler angles. It is an error to call this if +// has_components() returned false. //////////////////////////////////////////////////////////////////// INLINE const LVecBase3f &TransformState:: get_hpr() const { - check_components(); + check_hpr(); nassertr(has_hpr(), _hpr); return _hpr; } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::get_quat +// Access: Published +// Description: Returns the rotation component of the transform as a +// quaternion. It is an error to call this if +// has_components() returned false. +//////////////////////////////////////////////////////////////////// +INLINE const LQuaternionf &TransformState:: +get_quat() const { + check_quat(); + nassertr(has_quat(), _quat); + return _quat; +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::get_scale // Access: Published @@ -268,6 +351,20 @@ get_scale() const { return _scale; } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::get_uniform_scale +// Access: Published +// Description: Returns the scale component of the transform, as a +// single number. It is an error to call this if +// has_uniform_scale() returned false. +//////////////////////////////////////////////////////////////////// +INLINE float TransformState:: +get_uniform_scale() const { + check_components(); + nassertr(has_uniform_scale(), _scale[0]); + return _scale[0]; +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::get_mat // Access: Published @@ -309,6 +406,36 @@ check_components() const { } } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::check_hpr +// Access: Private +// Description: Ensures that we know the hpr of the transform +// (or that we know they cannot be derived). +//////////////////////////////////////////////////////////////////// +INLINE void TransformState:: +check_hpr() const { + // This pretends to be a const function, even though it's not, + // because it only updates a transparent cache value. + if ((_flags & F_hpr_known) == 0) { + ((TransformState *)this)->calc_hpr(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformState::check_quat +// Access: Private +// Description: Ensures that we know the quat of the transform +// (or that we know they cannot be derived). +//////////////////////////////////////////////////////////////////// +INLINE void TransformState:: +check_quat() const { + // This pretends to be a const function, even though it's not, + // because it only updates a transparent cache value. + if ((_flags & F_quat_known) == 0) { + ((TransformState *)this)->calc_quat(); + } +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::check_mat // Access: Private @@ -323,6 +450,21 @@ check_mat() const { } } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::check_uniform_scale +// Access: Private +// Description: Should be called immediately after _scale (and +// F_has_components) is set, this checks for a uniform +// scale and sets the bit appropriately. +//////////////////////////////////////////////////////////////////// +INLINE void TransformState:: +check_uniform_scale() { + if (IS_NEARLY_EQUAL(_scale[0], _scale[1]) && + IS_NEARLY_EQUAL(_scale[0], _scale[2])) { + _flags |= F_uniform_scale; + } +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::set_destructing // Access: Private diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index 6385be22ee..c5f5fbb9a6 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -206,7 +206,7 @@ TransformState:: bool TransformState:: operator < (const TransformState &other) const { static const int significant_flags = - (F_is_invalid | F_is_identity | F_components_given); + (F_is_invalid | F_is_identity | F_components_given | F_hpr_given); int flags = (_flags & significant_flags); int other_flags = (other._flags & significant_flags); @@ -220,7 +220,8 @@ operator < (const TransformState &other) const { return 0; } - if ((_flags & F_components_given) != 0) { + if ((_flags & (F_components_given | F_hpr_given)) == + (F_components_given | F_hpr_given)) { // If the transform was specified componentwise, compare them // componentwise. int c = _pos.compare_to(other._pos); @@ -301,7 +302,33 @@ make_pos_hpr_scale(const LVecBase3f &pos, const LVecBase3f &hpr, state->_pos = pos; state->_hpr = hpr; state->_scale = scale; - state->_flags = F_components_given | F_components_known | F_has_components; + 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 +// Access: Published, Static +// Description: Makes a new TransformState with the specified +// components. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +make_pos_quat_scale(const LVecBase3f &pos, const LQuaternionf &quat, + const LVecBase3f &scale) { + // 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)) { + return make_identity(); + } + + TransformState *state = new TransformState; + state->_pos = pos; + state->_quat = quat; + state->_scale = scale; + 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); } @@ -333,10 +360,14 @@ make_mat(const LMatrix4f &mat) { //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: set_pos(const LVecBase3f &pos) const { - if ((_flags & F_components_given) != 0) { + if (components_given()) { // If we started with a componentwise transform, we keep it that // way. - return make_pos_hpr_scale(pos, get_hpr(), get_scale()); + if (quat_given()) { + return make_pos_quat_scale(pos, get_quat(), get_scale()); + } else { + return make_pos_hpr_scale(pos, get_hpr(), get_scale()); + } } else { // Otherwise, we have a matrix transform, and we keep it that way. @@ -350,7 +381,7 @@ set_pos(const LVecBase3f &pos) const { // Function: TransformState::set_hpr // Access: Published // Description: Returns a new TransformState object that represents the -// original TransformState with its hpr component +// original TransformState with its rotation component // replaced with the indicated value, if possible. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: @@ -359,6 +390,19 @@ set_hpr(const LVecBase3f &hpr) const { return make_pos_hpr_scale(get_pos(), hpr, get_scale()); } +//////////////////////////////////////////////////////////////////// +// 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(has_components(), this); + return make_pos_quat_scale(get_pos(), quat, get_scale()); +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::set_scale // Access: Published @@ -369,7 +413,11 @@ set_hpr(const LVecBase3f &hpr) const { CPT(TransformState) TransformState:: set_scale(const LVecBase3f &scale) const { nassertr(has_components(), this); - return make_pos_hpr_scale(get_pos(), get_hpr(), scale); + if (quat_given()) { + return make_pos_quat_scale(get_pos(), get_quat(), scale); + } else { + return make_pos_hpr_scale(get_pos(), get_hpr(), scale); + } } //////////////////////////////////////////////////////////////////// @@ -549,13 +597,25 @@ output(ostream &out) const { out << lead << "pos " << get_pos(); lead = ' '; } - if (!get_hpr().almost_equal(LVecBase3f(0.0f, 0.0f, 0.0f))) { - out << lead << "hpr " << get_hpr(); - lead = ' '; + if (quat_given()) { + if (!get_quat().almost_equal(LQuaternionf::ident_quat())) { + out << lead << "quat " << get_quat(); + lead = ' '; + } + } else { + if (!get_hpr().almost_equal(LVecBase3f(0.0f, 0.0f, 0.0f))) { + out << lead << "hpr " << get_hpr(); + lead = ' '; + } } if (!get_scale().almost_equal(LVecBase3f(1.0f, 1.0f, 1.0f))) { - out << lead << "scale " << get_scale(); - lead = ' '; + if (has_uniform_scale()) { + out << lead << "scale " << get_uniform_scale(); + lead = ' '; + } else { + out << lead << "scale " << get_scale(); + lead = ' '; + } } if (lead == '(') { out << "(almost identity)"; @@ -702,8 +762,9 @@ calc_components() { if ((_flags & F_is_identity) != 0) { _scale.set(1.0f, 1.0f, 1.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; + _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 @@ -712,16 +773,57 @@ calc_components() { const LMatrix4f &mat = get_mat(); bool possible = decompose_matrix(mat, _scale, _hpr, _pos); - if (possible) { - // Some matrices can't be decomposed into scale, hpr, pos. - _flags |= F_has_components; + if (!possible) { + // Some matrices can't be decomposed into scale, hpr, pos. In + // this case, we now know that we cannot compute the components. + _flags |= F_components_known | F_hpr_known | F_quat_known; - // However, we can always get at least the pos. - mat.get_row3(_pos, 3); + } 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(); } - } - _flags |= F_components_known; + // 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; + } } //////////////////////////////////////////////////////////////////// @@ -739,7 +841,7 @@ calc_mat() { // 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); + compose_matrix(_mat, _scale, get_hpr(), _pos); } _flags |= F_mat_known; } @@ -778,10 +880,20 @@ write_datagram(BamWriter *manager, Datagram &dg) { } 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); - _hpr.write_datagram(dg); + if ((_flags & F_quat_given) != 0) { + _quat.write_datagram(dg); + } else { + get_hpr().write_datagram(dg); + } _scale.write_datagram(dg); } else { @@ -879,11 +991,19 @@ fillin(DatagramIterator &scan, BamReader *manager) { _flags = scan.get_uint16(); - if ((_flags & F_components_known) != 0) { + if ((_flags & F_components_given) != 0) { // Componentwise transform. _pos.read_datagram(scan); - _hpr.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); + check_uniform_scale(); } if ((_flags & F_mat_known) != 0) { diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h index 5b05db3763..39e6ab6910 100644 --- a/panda/src/pgraph/transformState.h +++ b/panda/src/pgraph/transformState.h @@ -70,6 +70,7 @@ PUBLISHED: static CPT(TransformState) make_invalid(); INLINE static CPT(TransformState) make_pos(const LVecBase3f &pos); INLINE static CPT(TransformState) make_hpr(const LVecBase3f &hpr); + INLINE static CPT(TransformState) make_quat(const LQuaternionf &quat); INLINE static CPT(TransformState) make_pos_hpr(const LVecBase3f &pos, const LVecBase3f &hpr); INLINE static CPT(TransformState) make_scale(float scale); @@ -77,6 +78,9 @@ PUBLISHED: static CPT(TransformState) make_pos_hpr_scale(const LVecBase3f &pos, const LVecBase3f &hpr, const LVecBase3f &scale); + static CPT(TransformState) make_pos_quat_scale(const LVecBase3f &pos, + const LQuaternionf &quat, + const LVecBase3f &scale); static CPT(TransformState) make_mat(const LMatrix4f &mat); INLINE bool is_identity() const; @@ -84,17 +88,24 @@ PUBLISHED: INLINE bool is_singular() const; INLINE bool has_components() const; INLINE bool components_given() const; + INLINE bool hpr_given() const; + INLINE bool quat_given() const; INLINE bool has_pos() const; INLINE bool has_hpr() const; + INLINE bool has_quat() const; INLINE bool has_scale() const; + INLINE bool has_uniform_scale() const; INLINE bool has_mat() const; INLINE const LVecBase3f &get_pos() const; INLINE const LVecBase3f &get_hpr() const; + INLINE const LQuaternionf &get_quat() const; INLINE const LVecBase3f &get_scale() const; + INLINE float get_uniform_scale() const; INLINE const LMatrix4f &get_mat() const; CPT(TransformState) set_pos(const LVecBase3f &pos) const; CPT(TransformState) set_hpr(const LVecBase3f &hpr) const; + CPT(TransformState) set_quat(const LQuaternionf &quat) const; CPT(TransformState) set_scale(const LVecBase3f &scale) const; CPT(TransformState) compose(const TransformState *other) const; @@ -144,26 +155,38 @@ private: // This is the actual data within the TransformState. INLINE void check_singular() const; INLINE void check_components() const; + INLINE void check_hpr() const; + INLINE void check_quat() const; INLINE void check_mat() const; void calc_singular(); void calc_components(); + void calc_hpr(); + void calc_quat(); void calc_mat(); + INLINE void check_uniform_scale(); + INLINE void set_destructing(); INLINE bool is_destructing() const; enum Flags { F_is_identity = 0x0001, F_is_singular = 0x0002, - F_singular_known = 0x0004, + F_singular_known = 0x0004, // set if we know F_is_singular F_components_given = 0x0008, - F_components_known = 0x0010, + F_components_known = 0x0010, // set if we know F_has_components F_has_components = 0x0020, - F_mat_known = 0x0040, + F_mat_known = 0x0040, // set if _mat is defined F_is_invalid = 0x0080, + F_quat_given = 0x0100, + F_quat_known = 0x0200, // set if _quat is defined + F_hpr_given = 0x0400, + F_hpr_known = 0x0800, // set if _hpr is defined + F_uniform_scale = 0x1000, F_is_destructing = 0x8000, }; LVecBase3f _pos, _hpr, _scale; + LQuaternionf _quat; LMatrix4f _mat; LMatrix4f *_inv_mat;