Browse Source

CompassEffects and node-relative BillboardEffects now work on cameras

David Rose 21 years ago
parent
commit
d46a88a76e

+ 8 - 0
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);

+ 101 - 40
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) 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;
+  CPT(TransformState) billboard_transform =
+    compute_billboard(net_transform, camera_transform);
 
-  // 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.
+  node_transform = billboard_transform->compose(node_transform);
+}
 
-  if (_eye_relative) {
-    up = _up_vector * rel_mat;
-    camera_pos = LVector3f::forward() * rel_mat;
+////////////////////////////////////////////////////////////////////
+//     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();
+}
 
-  } else {
-    up = _up_vector;
-    camera_pos = -(_look_at_point * rel_mat);
+////////////////////////////////////////////////////////////////////
+//     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;
   }
 
-  // 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);
-  }
+  CPT(TransformState) camera_transform = _look_at.get_net_transform();
 
-  // 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);
-  }
+  CPT(TransformState) billboard_transform =
+    compute_billboard(orig_net_transform, camera_transform);
 
-  node_transform = node_transform->compose(TransformState::make_mat(rotate));
+  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

+ 7 - 0
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;

+ 55 - 16
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;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 3 - 0
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;
 

+ 32 - 5
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);
   }

+ 28 - 0
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

+ 3 - 0
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;
 

+ 18 - 0
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);
+}

+ 41 - 0
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

+ 6 - 0
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<const RenderEffects *, indirect_less<const RenderEffects *> > 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;