diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index 3f4efddf26..cb883fbfc7 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -31,6 +31,7 @@ #include "lens.h" #include "perspectiveLens.h" #include "pointerTo.h" +#include "compassEffect.h" TypeHandle GraphicsOutput::_type_handle; @@ -627,6 +628,13 @@ make_cube_map(const string &name, int size, bool to_ram, } } + // Usually, we want the whole camera_rig to keep itself unrotated + // with respect to the world coordinate space, so the user can apply + // TexGenAttrib::M_world_cube_map to the objects on which the cube + // map texture is applied. If for some reason the user doesn't want + // this behavior, he can take this effect off again. + camera_rig.node()->set_effect(CompassEffect::make(NodePath())); + PT(Texture) tex = new Texture(name); tex->setup_cube_map(); tex->set_wrap_u(Texture::WM_clamp); diff --git a/panda/src/pgraph/billboardEffect.cxx b/panda/src/pgraph/billboardEffect.cxx index 2ce023c4a4..3d1aeacda5 100644 --- a/panda/src/pgraph/billboardEffect.cxx +++ b/panda/src/pgraph/billboardEffect.cxx @@ -131,8 +131,13 @@ void BillboardEffect:: cull_callback(CullTraverser *trav, CullTraverserData &data, CPT(TransformState) &node_transform, CPT(RenderState) &) const { - CPT(TransformState) net_transform = data._net_transform->compose(node_transform); - const TransformState *camera_transform = trav->get_camera_transform(); + CPT(TransformState) net_transform = data._net_transform; + if (net_transform->is_singular()) { + // If we're under a singular transform, never mind. + return; + } + + CPT(TransformState) camera_transform = trav->get_camera_transform(); // Determine the relative transform to our camera (or other look_at // coordinate space). @@ -140,53 +145,58 @@ cull_callback(CullTraverser *trav, CullTraverserData &data, camera_transform = _look_at.get_net_transform(); } - if (net_transform->is_singular()) { - // If we're under a singular transform, never mind. - return; - } + CPT(TransformState) billboard_transform = + compute_billboard(net_transform, camera_transform); - CPT(TransformState) rel_transform = - net_transform->invert_compose(camera_transform); - const LMatrix4f &rel_mat = rel_transform->get_mat(); - - // Determine the look_at point in the camera space. - LVector3f camera_pos, up; - - // If this is an eye-relative Billboard, then (a) the up vector is - // relative to the camera, not to the world, and (b) the look - // direction is towards the plane that contains the camera, - // perpendicular to the forward direction, not directly to the - // camera. - - if (_eye_relative) { - up = _up_vector * rel_mat; - camera_pos = LVector3f::forward() * rel_mat; - - } else { - up = _up_vector; - camera_pos = -(_look_at_point * rel_mat); - } - - // Now determine the rotation matrix for the Billboard. - LMatrix4f rotate; - if (_axial_rotate) { - heads_up(rotate, camera_pos, up); - } else { - look_at(rotate, camera_pos, up); - } - - // Also slide the billboard geometry towards the camera according to - // the offset factor. - if (_offset != 0.0f) { - LVector3f translate(rel_mat(3, 0), rel_mat(3, 1), rel_mat(3, 2)); - translate.normalize(); - translate *= _offset; - rotate.set_row(3, translate); - } - - node_transform = node_transform->compose(TransformState::make_mat(rotate)); + node_transform = billboard_transform->compose(node_transform); } +//////////////////////////////////////////////////////////////////// +// Function: BillboardEffect::has_net_transform +// Access: Public, Virtual +// Description: Should be overridden by derived classes to return +// true if net_transform() has been defined, and +// therefore the RenderEffect has some effect on the +// node's apparent net transform. +//////////////////////////////////////////////////////////////////// +bool BillboardEffect:: +has_net_transform() const { + // A BillboardEffect can only affect the net transform when it is to + // a particular node. A billboard to a camera is camera-dependent, + // of course, so it has no effect in the absence of any particular + // camera viewing it. + return !_look_at.is_empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: BillboardEffect::net_transform +// Access: Public, Virtual +// Description: Given the node's parent's net transform, compute its +// parent's new net transform after application of the +// RenderEffect. Presumably this interposes some +// special transform derived from the RenderEffect. +// This may only be called if has_net_transform(), +// above, has been defined to return true. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) BillboardEffect:: +net_transform(const TransformState *orig_net_transform) const { + // A BillboardEffect can only affect the net transform when it is to + // a particular node. A billboard to a camera is camera-dependent, + // of course, so it has no effect in the absence of any particular + // camera viewing it. + if (_look_at.is_empty()) { + return orig_net_transform; + } + + CPT(TransformState) camera_transform = _look_at.get_net_transform(); + + CPT(TransformState) billboard_transform = + compute_billboard(orig_net_transform, camera_transform); + + return orig_net_transform->compose(billboard_transform); +} + + //////////////////////////////////////////////////////////////////// // Function: BillboardEffect::compare_to_impl // Access: Protected, Virtual @@ -231,6 +241,57 @@ compare_to_impl(const RenderEffect *other) const { return 0; } +//////////////////////////////////////////////////////////////////// +// Function: BillboardEffect::compute_billboard +// Access: Private +// Description: Computes the billboard operation given the parent's +// net transform and the camera transform. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) BillboardEffect:: +compute_billboard(const TransformState *net_transform, + const TransformState *camera_transform) const { + CPT(TransformState) rel_transform = + net_transform->invert_compose(camera_transform); + const LMatrix4f &rel_mat = rel_transform->get_mat(); + + // Determine the look_at point in the camera space. + LVector3f camera_pos, up; + + // If this is an eye-relative Billboard, then (a) the up vector is + // relative to the camera, not to the world, and (b) the look + // direction is towards the plane that contains the camera, + // perpendicular to the forward direction, not directly to the + // camera. + + if (_eye_relative) { + up = _up_vector * rel_mat; + camera_pos = LVector3f::forward() * rel_mat; + + } else { + up = _up_vector; + camera_pos = -(_look_at_point * rel_mat); + } + + // Now determine the rotation matrix for the Billboard. + LMatrix4f rotate; + if (_axial_rotate) { + heads_up(rotate, camera_pos, up); + } else { + look_at(rotate, camera_pos, up); + } + + // Also slide the billboard geometry towards the camera according to + // the offset factor. + if (_offset != 0.0f) { + LVector3f translate(rel_mat(3, 0), rel_mat(3, 1), rel_mat(3, 2)); + translate.normalize(); + translate *= _offset; + rotate.set_row(3, translate); + } + + return TransformState::make_mat(rotate); +} + //////////////////////////////////////////////////////////////////// // Function: BillboardEffect::register_with_read_factory // Access: Public, Static diff --git a/panda/src/pgraph/billboardEffect.h b/panda/src/pgraph/billboardEffect.h index ba926a54ef..de5e39ca9d 100644 --- a/panda/src/pgraph/billboardEffect.h +++ b/panda/src/pgraph/billboardEffect.h @@ -63,9 +63,16 @@ public: CPT(TransformState) &node_transform, CPT(RenderState) &node_state) const; + virtual bool has_net_transform() const; + virtual CPT(TransformState) net_transform(const TransformState *orig_net_transform) const; + protected: virtual int compare_to_impl(const RenderEffect *other) const; +private: + CPT(TransformState) compute_billboard(const TransformState *net_transform, + const TransformState *camera_transform) const; + private: bool _off; LVector3f _up_vector; diff --git a/panda/src/pgraph/compassEffect.cxx b/panda/src/pgraph/compassEffect.cxx index 60328ab5e2..7b8026a50c 100644 --- a/panda/src/pgraph/compassEffect.cxx +++ b/panda/src/pgraph/compassEffect.cxx @@ -143,15 +143,59 @@ cull_callback(CullTraverser *, CullTraverserData &data, return; } - CPT(TransformState) net_transform = data._net_transform->compose(node_transform); + CPT(TransformState) true_net_transform = data._net_transform; + CPT(TransformState) want_transform = net_transform(data._net_transform); - // Compute the reference transform: our transform, as applied to the - // reference node. + // Now compute the transform that will convert true_net_transform to + // want_transform. This is inv(true_net_transform) * want_transform. + CPT(TransformState) compass_transform = + true_net_transform->invert_compose(want_transform); + + // And modify our local node's apparent transform so that + // true_net_transform->compose(new_node_transform) produces the same + // result we would have gotten had we actually computed + // want_transform->compose(orig_node_transform). + node_transform = node_transform->compose(compass_transform); +} + +//////////////////////////////////////////////////////////////////// +// Function: CompassEffect::has_net_transform +// Access: Public, Virtual +// Description: Should be overridden by derived classes to return +// true if net_transform() has been defined, and +// therefore the RenderEffect has some effect on the +// node's apparent net transform. +//////////////////////////////////////////////////////////////////// +bool CompassEffect:: +has_net_transform() const { + return (_properties != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CompassEffect::net_transform +// Access: Public, Virtual +// Description: Given the node's parent's net transform, compute its +// parent's new net transform after application of the +// RenderEffect. Presumably this interposes some +// special transform derived from the RenderEffect. +// This may only be called if has_net_transform(), +// above, has been defined to return true. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) CompassEffect:: +net_transform(const TransformState *orig_net_transform) const { + if (_properties == 0) { + // Nothing to do. + return orig_net_transform; + } + + // The reference transform: where we are acting as if we inherit + // from. Either the root node (identity) or the specified reference + // node. CPT(TransformState) ref_transform; if (_reference.is_empty()) { - ref_transform = node_transform; + ref_transform = TransformState::make_identity(); } else { - ref_transform = _reference.get_net_transform()->compose(node_transform); + ref_transform = _reference.get_net_transform(); } // Now compute the transform we actually want to achieve. This is @@ -166,7 +210,7 @@ cull_callback(CullTraverser *, CullTraverserData &data, } 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(); + LVecBase3f want_pos = orig_net_transform->get_pos(); const LVecBase3f &ref_pos = ref_transform->get_pos(); if ((_properties & P_x) != 0) { want_pos[0] = ref_pos[0]; @@ -180,7 +224,7 @@ cull_callback(CullTraverser *, CullTraverserData &data, 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); + want_transform = orig_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. @@ -189,7 +233,7 @@ cull_callback(CullTraverser *, CullTraverserData &data, } else { // For any other combination, we have to be able to decompose both // transforms. - if (!net_transform->has_components() || + if (!orig_net_transform->has_components() || !ref_transform->has_components()) { // If we can't decompose, just do the best we can: steal // everything but the pos. @@ -197,12 +241,12 @@ cull_callback(CullTraverser *, CullTraverserData &data, } else { // If we can decompose, then take only the components we want. - LQuaternionf want_quat = net_transform->get_quat(); + LQuaternionf want_quat = orig_net_transform->get_quat(); if ((_properties & P_rot) != 0) { want_quat = ref_transform->get_quat(); } - LVecBase3f want_scale = net_transform->get_scale(); + LVecBase3f want_scale = orig_net_transform->get_scale(); const LVecBase3f &ref_scale = ref_transform->get_scale(); if ((_properties & P_sx) != 0) { want_scale[0] = ref_scale[0]; @@ -220,12 +264,7 @@ cull_callback(CullTraverser *, CullTraverserData &data, } } - // Now compute the transform that will convert net_transform to - // want_transform. This is inv(net_transform) * want_transform. - CPT(TransformState) compass_transform = - net_transform->invert_compose(want_transform); - - node_transform = node_transform->compose(compass_transform); + return want_transform; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/compassEffect.h b/panda/src/pgraph/compassEffect.h index 70e10e29a0..c408ae29ad 100644 --- a/panda/src/pgraph/compassEffect.h +++ b/panda/src/pgraph/compassEffect.h @@ -88,6 +88,9 @@ public: CPT(TransformState) &node_transform, CPT(RenderState) &node_state) const; + virtual bool has_net_transform() const; + virtual CPT(TransformState) net_transform(const TransformState *orig_net_transform) const; + protected: virtual int compare_to_impl(const RenderEffect *other) const; diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index e2ef863ce2..6eaf359a00 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -658,8 +658,10 @@ get_transform(const NodePath &other) const { int a_count, b_count; if (find_common_ancestor(*this, other, a_count, b_count) == (NodePathComponent *)NULL) { if (allow_unrelated_wrt) { - pgraph_cat.debug() - << *this << " is not related to " << other << "\n"; + if (pgraph_cat.is_debug()) { + pgraph_cat.debug() + << *this << " is not related to " << other << "\n"; + } } else { pgraph_cat.error() << *this << " is not related to " << other << "\n"; @@ -667,8 +669,19 @@ get_transform(const NodePath &other) const { } } - CPT(TransformState) a_transform = r_get_partial_transform(_head, a_count); - CPT(TransformState) b_transform = r_get_partial_transform(other._head, b_count); + CPT(TransformState) a_transform, b_transform; + + a_transform = r_get_partial_transform(_head, a_count); + if (a_transform != (TransformState *)NULL) { + b_transform = r_get_partial_transform(other._head, b_count); + } + if (b_transform == (TransformState *)NULL) { + // If either path involved a node with a net_transform + // RenderEffect applied, we have to go all the way up to the root + // to get the right answer. + a_transform = r_get_net_transform(_head); + b_transform = r_get_net_transform(other._head); + } return b_transform->invert_compose(a_transform); } @@ -4804,8 +4817,15 @@ r_get_net_transform(NodePathComponent *comp) const { if (comp == (NodePathComponent *)NULL) { return TransformState::make_identity(); } else { + CPT(TransformState) net_transform = r_get_net_transform(comp->get_next()); CPT(TransformState) transform = comp->get_node()->get_transform(); - return r_get_net_transform(comp->get_next())->compose(transform); + + CPT(RenderEffects) effects = comp->get_node()->get_effects(); + if (effects->has_net_transform()) { + net_transform = effects->net_transform(net_transform); + } + + return net_transform->compose(transform); } } @@ -4816,12 +4836,19 @@ r_get_net_transform(NodePathComponent *comp) const { // indicated component node from the nth node above it. // If n exceeds the length of the path, this returns the // net transform from the root of the graph. +// +// If any node in the path had a net_transform effect +// applied, returns NULL--in this case the partial +// transform cannot be easily determined. //////////////////////////////////////////////////////////////////// CPT(TransformState) NodePath:: r_get_partial_transform(NodePathComponent *comp, int n) const { if (n == 0 || comp == (NodePathComponent *)NULL) { return TransformState::make_identity(); } else { + if (comp->get_node()->get_effects()->has_net_transform()) { + return NULL; + } CPT(TransformState) transform = comp->get_node()->get_transform(); return r_get_partial_transform(comp->get_next(), n - 1)->compose(transform); } diff --git a/panda/src/pgraph/renderEffect.cxx b/panda/src/pgraph/renderEffect.cxx index 66fc923aaf..87640c4710 100644 --- a/panda/src/pgraph/renderEffect.cxx +++ b/panda/src/pgraph/renderEffect.cxx @@ -161,6 +161,34 @@ cull_callback(CullTraverser *, CullTraverserData &, CPT(TransformState) &, CPT(RenderState) &) const { } +//////////////////////////////////////////////////////////////////// +// Function: RenderEffect::has_net_transform +// Access: Public, Virtual +// Description: Should be overridden by derived classes to return +// true if net_transform() has been defined, and +// therefore the RenderEffect has some effect on the +// node's apparent net transform. +//////////////////////////////////////////////////////////////////// +bool RenderEffect:: +has_net_transform() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: RenderEffect::net_transform +// Access: Public, Virtual +// Description: Given the node's parent's net transform, compute its +// parent's new net transform after application of the +// RenderEffect. Presumably this interposes some +// special transform derived from the RenderEffect. +// This may only be called if has_net_transform(), +// above, has been defined to return true. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) RenderEffect:: +net_transform(CPT(TransformState) &orig_net_transform) const { + return orig_net_transform; +} + //////////////////////////////////////////////////////////////////// // Function: RenderEffect::output // Access: Published, Virtual diff --git a/panda/src/pgraph/renderEffect.h b/panda/src/pgraph/renderEffect.h index 73e91585d5..753ead0647 100644 --- a/panda/src/pgraph/renderEffect.h +++ b/panda/src/pgraph/renderEffect.h @@ -75,6 +75,9 @@ public: CPT(TransformState) &node_transform, CPT(RenderState) &node_state) const; + virtual bool has_net_transform() const; + virtual CPT(TransformState) net_transform(CPT(TransformState) &orig_net_transform) const; + PUBLISHED: INLINE int compare_to(const RenderEffect &other) const; diff --git a/panda/src/pgraph/renderEffects.I b/panda/src/pgraph/renderEffects.I index 1aa768c7a3..92e078651e 100644 --- a/panda/src/pgraph/renderEffects.I +++ b/panda/src/pgraph/renderEffects.I @@ -199,3 +199,21 @@ has_cull_callback() const { } return ((_flags & F_has_cull_callback) != 0); } + +//////////////////////////////////////////////////////////////////// +// Function: RenderEffects::has_net_transform +// Access: Public +// Description: This function is provided as an optimization, to +// speed up the render-time checking for the existance +// of an effect with a compute_net_transform on this +// state. +//////////////////////////////////////////////////////////////////// +INLINE bool RenderEffects:: +has_net_transform() const { + if ((_flags & F_checked_net_transform) == 0) { + // We pretend this function is const, even though it transparently + // modifies the internal net_transform cache. + ((RenderEffects *)this)->determine_net_transform(); + } + return ((_flags & F_has_net_transform) != 0); +} diff --git a/panda/src/pgraph/renderEffects.cxx b/panda/src/pgraph/renderEffects.cxx index c121b07ce8..ff0fcfc370 100644 --- a/panda/src/pgraph/renderEffects.cxx +++ b/panda/src/pgraph/renderEffects.cxx @@ -420,6 +420,29 @@ cull_callback(CullTraverser *trav, CullTraverserData &data, } } +//////////////////////////////////////////////////////////////////// +// Function: RenderEffects::net_transform +// Access: Public +// Description: Calls net_transform() on all effects. You may check +// has_net_transform() first to see if any effects +// define this method to do anything useful. +// +// The order in which the individual effects are applied +// is not defined, so if more than one effect applies a +// change to the transform on any particular node, you +// might get indeterminate results. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) RenderEffects:: +net_transform(const TransformState *orig_net_transform) const { + CPT(TransformState) net_transform = orig_net_transform; + Effects::const_iterator ei; + for (ei = _effects.begin(); ei != _effects.end(); ++ei) { + net_transform = (*ei)._effect->net_transform(net_transform); + } + + return net_transform; +} + //////////////////////////////////////////////////////////////////// // Function: RenderEffects::get_num_states // Access: Published, Static @@ -573,6 +596,24 @@ determine_cull_callback() { } } +//////////////////////////////////////////////////////////////////// +// Function: RenderEffects::determine_net_transform +// Access: Private +// Description: This is the private implementation of has_net_transform(). +//////////////////////////////////////////////////////////////////// +void RenderEffects:: +determine_net_transform() { + _flags |= F_checked_net_transform; + + Effects::const_iterator ei; + for (ei = _effects.begin(); ei != _effects.end(); ++ei) { + if ((*ei)._effect->has_net_transform()) { + _flags |= F_has_net_transform; + return; + } + } +} + //////////////////////////////////////////////////////////////////// // Function: RenderEffects::register_with_read_factory // Access: Public, Static diff --git a/panda/src/pgraph/renderEffects.h b/panda/src/pgraph/renderEffects.h index 22f97012b3..d3d4044600 100644 --- a/panda/src/pgraph/renderEffects.h +++ b/panda/src/pgraph/renderEffects.h @@ -102,11 +102,15 @@ public: CPT(TransformState) &node_transform, CPT(RenderState) &node_state) const; + INLINE bool has_net_transform() const; + CPT(TransformState) net_transform(const TransformState *orig_net_transform) const; + private: static CPT(RenderEffects) return_new(RenderEffects *state); void determine_decal(); void determine_show_bounds(); void determine_cull_callback(); + void determine_net_transform(); private: typedef pset > States; @@ -144,6 +148,8 @@ private: F_has_show_bounds = 0x0008, F_checked_cull_callback = 0x0010, F_has_cull_callback = 0x0020, + F_checked_net_transform = 0x0040, + F_has_net_transform = 0x0080, }; int _flags;