From 00c4f5adabb7c860193c87c6a752134efd32d76d Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 17 Jul 2002 19:07:44 +0000 Subject: [PATCH] generalize CompassEffect --- panda/src/pgraph/billboardEffect.cxx | 16 --- panda/src/pgraph/billboardEffect.h | 1 - panda/src/pgraph/compassEffect.I | 27 ++++ panda/src/pgraph/compassEffect.cxx | 168 ++++++++++++++++++------- panda/src/pgraph/compassEffect.h | 51 +++++++- panda/src/pgraph/cullTraverserData.cxx | 58 ++++----- panda/src/pgraph/decalEffect.cxx | 16 --- panda/src/pgraph/decalEffect.h | 1 - 8 files changed, 220 insertions(+), 118 deletions(-) diff --git a/panda/src/pgraph/billboardEffect.cxx b/panda/src/pgraph/billboardEffect.cxx index 73c9720d2a..b76d5731d6 100644 --- a/panda/src/pgraph/billboardEffect.cxx +++ b/panda/src/pgraph/billboardEffect.cxx @@ -196,22 +196,6 @@ compare_to_impl(const RenderEffect *other) const { return 0; } -//////////////////////////////////////////////////////////////////// -// Function: BillboardEffect::make_default_impl -// Access: Protected, Virtual -// Description: Intended to be overridden by derived BillboardEffect -// types to specify what the default property for a -// BillboardEffect of this type should be. -// -// This should return a newly-allocated BillboardEffect of -// the same type that corresponds to whatever the -// standard default for this kind of BillboardEffect is. -//////////////////////////////////////////////////////////////////// -RenderEffect *BillboardEffect:: -make_default_impl() const { - return new BillboardEffect; -} - //////////////////////////////////////////////////////////////////// // Function: BillboardEffect::register_with_read_factory // Access: Public, Static diff --git a/panda/src/pgraph/billboardEffect.h b/panda/src/pgraph/billboardEffect.h index fa5ff9bf05..ff614c3801 100644 --- a/panda/src/pgraph/billboardEffect.h +++ b/panda/src/pgraph/billboardEffect.h @@ -63,7 +63,6 @@ public: protected: virtual int compare_to_impl(const RenderEffect *other) const; - virtual RenderEffect *make_default_impl() const; private: bool _off; diff --git a/panda/src/pgraph/compassEffect.I b/panda/src/pgraph/compassEffect.I index afa66b95c3..3046b2c081 100644 --- a/panda/src/pgraph/compassEffect.I +++ b/panda/src/pgraph/compassEffect.I @@ -25,4 +25,31 @@ //////////////////////////////////////////////////////////////////// INLINE CompassEffect:: CompassEffect() { + _properties = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CompassEffect::get_reference +// Access: Published +// Description: Returns the reference node from which the +// CompassEffect inherits its transform. If this is +// empty, it means the root of the scene graph. +//////////////////////////////////////////////////////////////////// +INLINE const NodePath &CompassEffect:: +get_reference() const { + return _reference; +} + +//////////////////////////////////////////////////////////////////// +// Function: CompassEffect::get_properties +// Access: Published +// Description: + +// Returns the bitmask of properties that this +// CompassEffect object inherits from its reference node +// (or from the root). +//////////////////////////////////////////////////////////////////// +INLINE int CompassEffect:: +get_properties() const { + return _properties; } diff --git a/panda/src/pgraph/compassEffect.cxx b/panda/src/pgraph/compassEffect.cxx index b8be381562..8d9e2939e5 100644 --- a/panda/src/pgraph/compassEffect.cxx +++ b/panda/src/pgraph/compassEffect.cxx @@ -29,12 +29,19 @@ TypeHandle CompassEffect::_type_handle; //////////////////////////////////////////////////////////////////// // Function: CompassEffect::make // Access: Published, Static -// Description: Constructs a new CompassEffect object. +// Description: Constructs a new CompassEffect object. If the +// reference is an empty NodePath, it means the +// CompassEffect is relative to the root of the scene +// graph; otherwise, it's relative to the indicated +// node. The properties bitmask specifies the set of +// properties that the compass node inherits from the +// reference instead of from its parent. //////////////////////////////////////////////////////////////////// CPT(RenderEffect) CompassEffect:: -make(const NodePath &reference) { +make(const NodePath &reference, int properties) { CompassEffect *effect = new CompassEffect; effect->_reference = reference; + effect->_properties = (properties & P_all); return return_new(effect); } @@ -57,13 +64,41 @@ safe_to_transform() const { //////////////////////////////////////////////////////////////////// void CompassEffect:: output(ostream &out) const { - if (!_reference.is_empty()) { - RenderEffect::output(out); + out << get_type() << ":"; + if (_properties == 0) { + out << " none"; + } + if ((_properties & P_pos) == P_pos) { + out << " xyz"; } else { - out << get_type() << ":"; - if (!_reference.is_empty()) { - out << " reference " << _reference; + if ((_properties & P_x) != 0) { + out << " x"; } + if ((_properties & P_y) != 0) { + out << " y"; + } + if ((_properties & P_z) != 0) { + out << " z"; + } + } + if ((_properties & P_rot) != 0) { + out << " rot"; + } + if ((_properties & P_scale) == P_scale) { + out << " scale"; + } else { + if ((_properties & P_sx) != 0) { + out << " sx"; + } + if ((_properties & P_sy) != 0) { + out << " sy"; + } + if ((_properties & P_sz) != 0) { + out << " sz"; + } + } + if (!_reference.is_empty()) { + out << " reference " << _reference; } } @@ -77,35 +112,85 @@ output(ostream &out) const { CPT(TransformState) CompassEffect:: do_compass(const TransformState *net_transform, const TransformState *node_transform) const { - if (!net_transform->has_components() || !node_transform->has_quat()) { - // If we don't have a decomposable transform, we can't do anything here. - pgraph_cat.warning() - << "CompassEffect unable to adjust non-decomposable transform\n"; + if (_properties == 0) { + // Nothing to do. return TransformState::make_identity(); } - // Compute just the rotation part of the transform we want. - CPT(TransformState) want_rot = TransformState::make_identity(); - if (!_reference.is_empty()) { - CPT(TransformState) rel_transform = _reference.get_net_transform(); - if (!rel_transform->has_quat()) { - pgraph_cat.warning() - << "CompassEffect unable to reference non-decomposable transform\n"; - } else { - want_rot = TransformState::make_quat(rel_transform->get_quat()); - } + // Compute the reference transform: our transform, as applied to the + // reference node. + CPT(TransformState) ref_transform; + if (_reference.is_empty()) { + ref_transform = node_transform; + } else { + ref_transform = _reference.get_net_transform()->compose(node_transform); } - want_rot = - want_rot->compose(TransformState::make_quat(node_transform->get_quat())); + // Now compute the transform we actually want to achieve. This is + // all of the components from the net transform we want to inherit + // normally from our parent, with all of the components from the ref + // transform we want to inherit from our reference. + CPT(TransformState) want_transform; + if (_properties == P_all) { + // If we want to steal the whole transform, that's easy. + want_transform = ref_transform; - // Now compute the net transform we want to achieve. This is the - // same as the net transform we were given, except the rotation - // component is replaced by our desired rotation. - CPT(TransformState) want_transform = - TransformState::make_pos_quat_scale(net_transform->get_pos(), - want_rot->get_quat(), - net_transform->get_scale()); + } else { + // How much of the pos do we want to steal? We can always + // determine a transform's pos, even if it's nondecomposable. + LVecBase3f want_pos = net_transform->get_pos(); + const LVecBase3f &ref_pos = ref_transform->get_pos(); + if ((_properties & P_x) != 0) { + want_pos[0] = ref_pos[0]; + } + if ((_properties & P_y) != 0) { + want_pos[1] = ref_pos[1]; + } + if ((_properties & P_z) != 0) { + want_pos[2] = ref_pos[2]; + } + + if ((_properties & ~P_pos) == 0) { + // If we only want to steal the pos, that's pretty easy. + want_transform = net_transform->set_pos(want_pos); + + } else if ((_properties & (P_rot | P_scale)) == (P_rot | P_scale)) { + // If we want to steal everything *but* the pos, also easy. + want_transform = ref_transform->set_pos(want_pos); + + } else { + // For any other combination, we have to be able to decompose both + // transforms. + if (!net_transform->has_components() || + !ref_transform->has_components()) { + // If we can't decompose, just do the best we can: steal + // everything but the pos. + want_transform = ref_transform->set_pos(want_pos); + + } else { + // If we can decompose, then take only the components we want. + LQuaternionf want_quat = net_transform->get_quat(); + if ((_properties & P_rot) != 0) { + want_quat = ref_transform->get_quat(); + } + + LVecBase3f want_scale = net_transform->get_scale(); + const LVecBase3f &ref_scale = ref_transform->get_scale(); + if ((_properties & P_sx) != 0) { + want_scale[0] = ref_scale[0]; + } + if ((_properties & P_sy) != 0) { + want_scale[1] = ref_scale[1]; + } + if ((_properties & P_sz) != 0) { + want_scale[2] = ref_scale[2]; + } + + want_transform = + TransformState::make_pos_quat_scale(want_pos, want_quat, want_scale); + } + } + } // Now compute the transform that will convert net_transform to // want_transform. This is inv(net_transform) * want_transform. @@ -132,6 +217,9 @@ compare_to_impl(const RenderEffect *other) const { const CompassEffect *ta; DCAST_INTO_R(ta, other, 0); + if (_properties != ta->_properties) { + return _properties - ta->_properties; + } int compare = _reference.compare_to(ta->_reference); if (compare != 0) { return compare; @@ -139,22 +227,6 @@ compare_to_impl(const RenderEffect *other) const { return 0; } -//////////////////////////////////////////////////////////////////// -// Function: CompassEffect::make_default_impl -// Access: Protected, Virtual -// Description: Intended to be overridden by derived CompassEffect -// types to specify what the default property for a -// CompassEffect of this type should be. -// -// This should return a newly-allocated CompassEffect of -// the same type that corresponds to whatever the -// standard default for this kind of CompassEffect is. -//////////////////////////////////////////////////////////////////// -RenderEffect *CompassEffect:: -make_default_impl() const { - return new CompassEffect; -} - //////////////////////////////////////////////////////////////////// // Function: CompassEffect::register_with_read_factory // Access: Public, Static @@ -175,6 +247,9 @@ register_with_read_factory() { void CompassEffect:: write_datagram(BamWriter *manager, Datagram &dg) { RenderEffect::write_datagram(manager, dg); + dg.add_uint16(_properties); + // *** We don't write out the _reference NodePath right now. Maybe + // we should. } //////////////////////////////////////////////////////////////////// @@ -207,4 +282,5 @@ make_from_bam(const FactoryParams ¶ms) { void CompassEffect:: fillin(DatagramIterator &scan, BamReader *manager) { RenderEffect::fillin(scan, manager); + _properties = scan.get_uint16(); } diff --git a/panda/src/pgraph/compassEffect.h b/panda/src/pgraph/compassEffect.h index 1f78e03a98..ad41d668ff 100644 --- a/panda/src/pgraph/compassEffect.h +++ b/panda/src/pgraph/compassEffect.h @@ -27,16 +27,57 @@ //////////////////////////////////////////////////////////////////// // Class : CompassEffect -// Description : Indicates that geometry at this node should -// automatically rotate to face the camera, or any other -// arbitrary node. +// Description : A CompassEffect causes a node to inherit its rotation +// (or pos or scale, if specified) from some other +// reference node in the graph, or more often from the +// root. +// +// In its purest form, a CompassEffect is used to keep +// the node's rotation fixed relative to the top of the +// scene graph, despite other transforms that may exist +// above the node. Hence the name: the node behaves +// like a magnetic compass, always pointing in the same +// direction. +// +// As an couple of generalizing extensions, the +// CompassEffect may also be set up to always orient its +// node according to some other reference node than the +// root of the scene graph. Furthermore, it may +// optionally adjust any of pos, rotation, or scale, +// instead of necessarily rotation; and it may adjust +// individual pos and scale components. (Rotation may +// not be adjusted on an individual component basis; +// that's just asking for trouble.) +// +// Be careful when using the pos and scale modes. In +// these modes, it's possible for the CompassEffect to +// move its node far from its normal bounding volume, +// causing culling to fail. If this is an issue, you +// may need to explicitly set a large (or infinite) +// bounding volume on the effect node. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA CompassEffect : public RenderEffect { private: INLINE CompassEffect(); PUBLISHED: - static CPT(RenderEffect) make(const NodePath &reference); + enum Properties { + P_x = 0x001, + P_y = 0x002, + P_z = 0x004, + P_pos = 0x007, + P_rot = 0x008, + P_sx = 0x010, + P_sy = 0x020, + P_sz = 0x040, + P_scale = 0x070, + P_all = 0x07f, + }; + static CPT(RenderEffect) make(const NodePath &reference, + int properties = P_rot); + + INLINE const NodePath &get_reference() const; + INLINE int get_properties() const; public: virtual bool safe_to_transform() const; @@ -48,10 +89,10 @@ public: protected: virtual int compare_to_impl(const RenderEffect *other) const; - virtual RenderEffect *make_default_impl() const; private: NodePath _reference; + int _properties; public: static void register_with_read_factory(); diff --git a/panda/src/pgraph/cullTraverserData.cxx b/panda/src/pgraph/cullTraverserData.cxx index 89aa76fa4a..0b799b7cb9 100644 --- a/panda/src/pgraph/cullTraverserData.cxx +++ b/panda/src/pgraph/cullTraverserData.cxx @@ -35,11 +35,33 @@ //////////////////////////////////////////////////////////////////// void CullTraverserData:: apply_transform_and_state(CullTraverser *trav) { - const TransformState *node_transform = node()->get_transform(); + CPT(TransformState) node_transform = node()->get_transform(); + + // First, compute the _net_transform, because we need it for the + // compass and billboard effects. + _net_transform = _net_transform->compose(node_transform); + + const RenderEffects *node_effects = node()->get_effects(); + + const CompassEffect *compass = node_effects->get_compass(); + if (compass != (const CompassEffect *)NULL) { + CPT(TransformState) compass_transform = + compass->do_compass(_net_transform, node_transform); + _net_transform = _net_transform->compose(compass_transform); + node_transform = node_transform->compose(compass_transform); + } + + const BillboardEffect *billboard = node_effects->get_billboard(); + if (billboard != (const BillboardEffect *)NULL) { + CPT(TransformState) billboard_transform = + billboard->do_billboard(_net_transform, trav->get_camera_transform()); + _net_transform = _net_transform->compose(billboard_transform); + node_transform = node_transform->compose(billboard_transform); + } + if (!node_transform->is_identity()) { _render_transform = _render_transform->compose(node_transform); - _net_transform = _net_transform->compose(node_transform); - + if ((_view_frustum != (GeometricBoundingVolume *)NULL) || (_guard_band != (GeometricBoundingVolume *)NULL)) { // We need to move the viewing frustums into the node's @@ -72,36 +94,6 @@ apply_transform_and_state(CullTraverser *trav) { const RenderState *node_state = node()->get_state(); _state = _state->compose(node_state); - - const RenderEffects *node_effects = node()->get_effects(); - - const CompassEffect *compass = node_effects->get_compass(); - if (compass != (const CompassEffect *)NULL) { - // Got to apply a compass transform here. - CPT(TransformState) compass_transform = - compass->do_compass(_net_transform, node_transform); - _render_transform = _render_transform->compose(compass_transform); - _net_transform = _net_transform->compose(compass_transform); - - // We can't reliably cull within a compass, because the geometry - // might get rotated out of its bounding volume. So once we get - // within a compass, we consider it all visible. - _view_frustum = (GeometricBoundingVolume *)NULL; - } - - const BillboardEffect *billboard = node_effects->get_billboard(); - if (billboard != (const BillboardEffect *)NULL) { - // Got to apply a billboard transform here. - CPT(TransformState) billboard_transform = - billboard->do_billboard(_net_transform, trav->get_camera_transform()); - _render_transform = _render_transform->compose(billboard_transform); - _net_transform = _net_transform->compose(billboard_transform); - - // We can't reliably cull within a billboard, because the geometry - // might get rotated out of its bounding volume. So once we get - // within a billboard, we consider it all visible. - _view_frustum = (GeometricBoundingVolume *)NULL; - } } diff --git a/panda/src/pgraph/decalEffect.cxx b/panda/src/pgraph/decalEffect.cxx index 03f324d213..912c5336f7 100644 --- a/panda/src/pgraph/decalEffect.cxx +++ b/panda/src/pgraph/decalEffect.cxx @@ -69,22 +69,6 @@ compare_to_impl(const RenderEffect *other) const { return 0; } -//////////////////////////////////////////////////////////////////// -// Function: DecalEffect::make_default_impl -// Access: Protected, Virtual -// Description: Intended to be overridden by derived DecalEffect -// types to specify what the default property for a -// DecalEffect of this type should be. -// -// This should return a newly-allocated DecalEffect of -// the same type that corresponds to whatever the -// standard default for this kind of DecalEffect is. -//////////////////////////////////////////////////////////////////// -RenderEffect *DecalEffect:: -make_default_impl() const { - return new DecalEffect; -} - //////////////////////////////////////////////////////////////////// // Function: DecalEffect::register_with_read_factory // Access: Public, Static diff --git a/panda/src/pgraph/decalEffect.h b/panda/src/pgraph/decalEffect.h index 148b005601..87cb53cae3 100644 --- a/panda/src/pgraph/decalEffect.h +++ b/panda/src/pgraph/decalEffect.h @@ -39,7 +39,6 @@ PUBLISHED: protected: virtual bool safe_to_combine() const; virtual int compare_to_impl(const RenderEffect *other) const; - virtual RenderEffect *make_default_impl() const; public: static void register_with_read_factory();