Browse Source

separate out RenderEffect from RenderAttrib

David Rose 24 years ago
parent
commit
9a7e4b319c

+ 7 - 7
panda/src/egg2pg/qpeggLoader.cxx

@@ -26,11 +26,11 @@
 #include "textureAttrib.h"
 #include "textureApplyAttrib.h"
 #include "texturePool.h"
-#include "billboardAttrib.h"
+#include "billboardEffect.h"
 #include "cullFaceAttrib.h"
 #include "cullBinAttrib.h"
 #include "transparencyAttrib.h"
-#include "decalAttrib.h"
+#include "decalEffect.h"
 #include "depthTestAttrib.h"
 #include "depthWriteAttrib.h"
 #include "materialAttrib.h"
@@ -197,8 +197,8 @@ reparent_decals() {
         }
       }
 
-      // Finally, set the DecalAttrib on the base geometry.
-      geom_parent.node()->set_attrib(DecalAttrib::make());
+      // Finally, set the DecalEffect on the base geometry.
+      geom_parent.node()->set_effect(DecalEffect::make());
     }
   }
 }
@@ -1428,15 +1428,15 @@ create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
   // If the group has a billboard flag, apply that.
   switch (egg_group->get_billboard_type()) {
   case EggGroup::BT_point_camera_relative:
-    node->set_attrib(BillboardAttrib::make_point_eye());
+    node->set_effect(BillboardEffect::make_point_eye());
     break;
 
   case EggGroup::BT_point_world_relative:
-    node->set_attrib(BillboardAttrib::make_point_world());
+    node->set_effect(BillboardEffect::make_point_world());
     break;
 
   case EggGroup::BT_axis:
-    node->set_attrib(BillboardAttrib::make_axis());
+    node->set_effect(BillboardEffect::make_axis());
     break;
 
   case EggGroup::BT_none:

+ 12 - 6
panda/src/pgraph/Sources.pp

@@ -6,7 +6,7 @@
   #define TARGET pgraph
   
   #define SOURCES \
-    billboardAttrib.h billboardAttrib.I \
+    billboardEffect.h billboardEffect.I \
     binCullHandler.h binCullHandler.I \
     qpcamera.h qpcamera.I \
     colorAttrib.h colorAttrib.I \
@@ -23,7 +23,7 @@
     qpcullTraverser.h qpcullTraverser.I \
     cullTraverserData.h cullTraverserData.I \
     cullableObject.h cullableObject.I \
-    decalAttrib.h decalAttrib.I \
+    decalEffect.h decalEffect.I \
     depthTestAttrib.h depthTestAttrib.I \
     depthWriteAttrib.h depthWriteAttrib.I \
     drawCullHandler.h drawCullHandler.I \
@@ -39,6 +39,8 @@
     qpnodePathComponent.h qpnodePathComponent.I \
     pandaNode.h pandaNode.I \
     renderAttrib.h renderAttrib.I \
+    renderEffect.h renderEffect.I \
+    renderEffects.h renderEffects.I \
     renderState.h renderState.I \
     selectiveChildNode.h selectiveChildNode.I \
     qpsequenceNode.h qpsequenceNode.I \
@@ -49,7 +51,7 @@
 
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx    
   #define INCLUDED_SOURCES \
-    billboardAttrib.cxx \
+    billboardEffect.cxx \
     binCullHandler.cxx \
     qpcamera.cxx \
     colorAttrib.cxx \
@@ -66,7 +68,7 @@
     qpcullTraverser.cxx \
     cullTraverserData.cxx \
     cullableObject.cxx \
-    decalAttrib.cxx \
+    decalEffect.cxx \
     depthTestAttrib.cxx \
     depthWriteAttrib.cxx \
     drawCullHandler.cxx \
@@ -82,6 +84,8 @@
     qpnodePathComponent.cxx \
     pandaNode.cxx \
     renderAttrib.cxx \
+    renderEffect.cxx \
+    renderEffects.cxx \
     renderState.cxx \
     selectiveChildNode.cxx \
     qpsequenceNode.cxx \
@@ -97,7 +101,7 @@
   #endif
 
   #define INSTALL_HEADERS \
-    billboardAttrib.h billboardAttrib.I \
+    billboardEffect.h billboardEffect.I \
     binCullHandler.h binCullHandler.I \
     qpcamera.h qpcamera.I \
     colorAttrib.h colorAttrib.I \
@@ -114,7 +118,7 @@
     qpcullTraverser.h qpcullTraverser.I \
     cullTraverserData.h cullTraverserData.I \
     cullableObject.h cullableObject.I \
-    decalAttrib.h decalAttrib.I \
+    decalEffect.h decalEffect.I \
     depthTestAttrib.h depthTestAttrib.I \
     depthWriteAttrib.h depthWriteAttrib.I \
     drawCullHandler.h drawCullHandler.I \
@@ -127,6 +131,8 @@
     qpnodePathComponent.h qpnodePathComponent.I \
     pandaNode.h pandaNode.I \
     renderAttrib.h renderAttrib.I \
+    renderEffect.h renderEffect.I \
+    renderEffects.h renderEffects.I \
     renderState.h renderState.I \
     selectiveChildNode.h selectiveChildNode.I \
     qpsequenceNode.h qpsequenceNode.I \

+ 30 - 30
panda/src/pgraph/billboardAttrib.I → panda/src/pgraph/billboardEffect.I

@@ -1,5 +1,5 @@
-// Filename: billboardAttrib.I
-// Created by:  drose (27Feb02)
+// Filename: billboardEffect.I
+// Created by:  drose (14Mar02)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -18,136 +18,136 @@
 
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::Constructor
+//     Function: BillboardEffect::Constructor
 //       Access: Private
-//  Description: Use BillboardAttrib::make() to construct a new
-//               BillboardAttrib object.
+//  Description: Use BillboardEffect::make() to construct a new
+//               BillboardEffect object.
 ////////////////////////////////////////////////////////////////////
-INLINE BillboardAttrib::
-BillboardAttrib() {
+INLINE BillboardEffect::
+BillboardEffect() {
   _off = true;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::make_axis
+//     Function: BillboardEffect::make_axis
 //       Access: Published, Static
 //  Description: A convenience function to make a typical
 //               axis-rotating billboard.
 ////////////////////////////////////////////////////////////////////
-INLINE CPT(RenderAttrib) BillboardAttrib::
+INLINE CPT(RenderEffect) BillboardEffect::
 make_axis() {
   return make(LVector3f::up(), false, true, 
               0.0f, qpNodePath(), LPoint3f(0.0f, 0.0f, 0.0f));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::make_point_eye
+//     Function: BillboardEffect::make_point_eye
 //       Access: Published, Static
 //  Description: A convenience function to make a typical
 //               eye-relative point-rotating billboard.
 ////////////////////////////////////////////////////////////////////
-INLINE CPT(RenderAttrib) BillboardAttrib::
+INLINE CPT(RenderEffect) BillboardEffect::
 make_point_eye() {
   return make(LVector3f::up(), true, false,
               0.0f, qpNodePath(), LPoint3f(0.0f, 0.0f, 0.0f));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::make_point_world
+//     Function: BillboardEffect::make_point_world
 //       Access: Published, Static
 //  Description: A convenience function to make a typical
 //               world-relative point-rotating billboard.
 ////////////////////////////////////////////////////////////////////
-INLINE CPT(RenderAttrib) BillboardAttrib::
+INLINE CPT(RenderEffect) BillboardEffect::
 make_point_world() {
   return make(LVector3f::up(), false, false,
               0.0f, qpNodePath(), LPoint3f(0.0f, 0.0f, 0.0f));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::is_off
+//     Function: BillboardEffect::is_off
 //       Access: Published
-//  Description: Returns true if the BillboardAttrib is an 'off'
-//               BillboardAttrib, indicating that it does not enable
-//               billboarding.  This kind of BillboardAttrib isn't
+//  Description: Returns true if the BillboardEffect is an 'off'
+//               BillboardEffect, indicating that it does not enable
+//               billboarding.  This kind of BillboardEffect isn't
 //               particularly useful and isn't normally created or
 //               stored in the graph; it might be implicitly
 //               discovered as the result of a
 //               qpNodePath::get_rel_state().
 ////////////////////////////////////////////////////////////////////
-INLINE bool BillboardAttrib::
+INLINE bool BillboardEffect::
 is_off() const {
   return _off;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::get_up_vector
+//     Function: BillboardEffect::get_up_vector
 //       Access: Published
 //  Description: Returns the up vector in effect for this billboard.
 ////////////////////////////////////////////////////////////////////
-INLINE const LVector3f &BillboardAttrib::
+INLINE const LVector3f &BillboardEffect::
 get_up_vector() const {
   return _up_vector;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::get_eye_relative
+//     Function: BillboardEffect::get_eye_relative
 //       Access: Published
 //  Description: Returns true if this billboard interprets the up
 //               vector relative to the camera, or false if it is
 //               relative to the world.
 ////////////////////////////////////////////////////////////////////
-INLINE bool BillboardAttrib::
+INLINE bool BillboardEffect::
 get_eye_relative() const {
   return _eye_relative;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::get_axial_rotate
+//     Function: BillboardEffect::get_axial_rotate
 //       Access: Published
 //  Description: Returns true if this billboard rotates only around
 //               the axis of the up vector, or false if it rotates
 //               freely in three dimensions.
 ////////////////////////////////////////////////////////////////////
-INLINE bool BillboardAttrib::
+INLINE bool BillboardEffect::
 get_axial_rotate() const {
   return _axial_rotate;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::get_offset
+//     Function: BillboardEffect::get_offset
 //       Access: Published
 //  Description: Returns the distance toward the camera (or the
 //               look_at_point) the billboard is moved towards, after
 //               rotating.  This can be used to ensure the billboard
 //               is not obscured by nearby geometry.
 ////////////////////////////////////////////////////////////////////
-INLINE float BillboardAttrib::
+INLINE float BillboardEffect::
 get_offset() const {
   return _offset;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::get_look_at
+//     Function: BillboardEffect::get_look_at
 //       Access: Published
 //  Description: Returns the node this billboard will rotate to look
 //               towards.  If this is empty, it means the billboard
 //               will rotate towards the current camera node, wherever
 //               that might be.
 ////////////////////////////////////////////////////////////////////
-INLINE const qpNodePath &BillboardAttrib::
+INLINE const qpNodePath &BillboardEffect::
 get_look_at() const {
   return _look_at;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::get_look_at_point
+//     Function: BillboardEffect::get_look_at_point
 //       Access: Published
 //  Description: Returns the point, relative to the look_at node,
 //               towards which the billboard will rotate.  Normally
 //               this is (0, 0, 0).
 ////////////////////////////////////////////////////////////////////
-INLINE const LPoint3f &BillboardAttrib::
+INLINE const LPoint3f &BillboardEffect::
 get_look_at_point() const {
   return _look_at_point;
 }

+ 64 - 52
panda/src/pgraph/billboardAttrib.cxx → panda/src/pgraph/billboardEffect.cxx

@@ -1,5 +1,5 @@
-// Filename: billboardAttrib.cxx
-// Created by:  drose (27Feb02)
+// Filename: billboardEffect.cxx
+// Created by:  drose (14Mar02)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -16,42 +16,54 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#include "billboardAttrib.h"
+#include "billboardEffect.h"
 #include "look_at.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "datagram.h"
 #include "datagramIterator.h"
 
-TypeHandle BillboardAttrib::_type_handle;
+TypeHandle BillboardEffect::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::make
+//     Function: BillboardEffect::make
 //       Access: Published, Static
-//  Description: Constructs a new BillboardAttrib object with the
+//  Description: Constructs a new BillboardEffect object with the
 //               indicated properties.
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) BillboardAttrib::
+CPT(RenderEffect) BillboardEffect::
 make(const LVector3f &up_vector, bool eye_relative,
      bool axial_rotate, float offset, const qpNodePath &look_at,
      const LPoint3f &look_at_point) {
-  BillboardAttrib *attrib = new BillboardAttrib;
-  attrib->_up_vector = up_vector;
-  attrib->_eye_relative = eye_relative;
-  attrib->_axial_rotate = axial_rotate;
-  attrib->_offset = offset;
-  attrib->_look_at = look_at;
-  attrib->_look_at_point = look_at_point;
-  attrib->_off = false;
-  return return_new(attrib);
+  BillboardEffect *effect = new BillboardEffect;
+  effect->_up_vector = up_vector;
+  effect->_eye_relative = eye_relative;
+  effect->_axial_rotate = axial_rotate;
+  effect->_offset = offset;
+  effect->_look_at = look_at;
+  effect->_look_at_point = look_at_point;
+  effect->_off = false;
+  return return_new(effect);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::output
+//     Function: BillboardEffect::safe_to_combine
+//       Access: Public, Virtual
+//  Description: Returns true if this kind of effect can safely be
+//               combined with sibling nodes that share the exact same
+//               effect, or false if this is not a good idea.
+////////////////////////////////////////////////////////////////////
+bool BillboardEffect::
+safe_to_combine() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BillboardEffect::output
 //       Access: Public, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
-void BillboardAttrib::
+void BillboardEffect::
 output(ostream &out) const {
   out << get_type() << ":";
   if (is_off()) {
@@ -82,13 +94,13 @@ output(ostream &out) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::do_billboard
+//     Function: BillboardEffect::do_billboard
 //       Access: Public
 //  Description: Computes the appropriate transform to apply to the
 //               billboarded geometry, given its current net
 //               transform, and the camera's inverse net transform.
 ////////////////////////////////////////////////////////////////////
-CPT(TransformState) BillboardAttrib::
+CPT(TransformState) BillboardEffect::
 do_billboard(const TransformState *net_transform,
              const TransformState *camera_transform) const {
   // Determine the relative transform to our camera (or other look_at
@@ -141,23 +153,23 @@ do_billboard(const TransformState *net_transform,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::compare_to_impl
+//     Function: BillboardEffect::compare_to_impl
 //       Access: Protected, Virtual
-//  Description: Intended to be overridden by derived BillboardAttrib
+//  Description: Intended to be overridden by derived BillboardEffect
 //               types to return a unique number indicating whether
-//               this BillboardAttrib is equivalent to the other one.
+//               this BillboardEffect is equivalent to the other one.
 //
-//               This should return 0 if the two BillboardAttrib objects
+//               This should return 0 if the two BillboardEffect objects
 //               are equivalent, a number less than zero if this one
 //               should be sorted before the other one, and a number
 //               greater than zero otherwise.
 //
-//               This will only be called with two BillboardAttrib
+//               This will only be called with two BillboardEffect
 //               objects whose get_type() functions return the same.
 ////////////////////////////////////////////////////////////////////
-int BillboardAttrib::
-compare_to_impl(const RenderAttrib *other) const {
-  const BillboardAttrib *ta;
+int BillboardEffect::
+compare_to_impl(const RenderEffect *other) const {
+  const BillboardEffect *ta;
   DCAST_INTO_R(ta, other, 0);
 
   if (_axial_rotate != ta->_axial_rotate) {
@@ -185,41 +197,41 @@ compare_to_impl(const RenderAttrib *other) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::make_default_impl
+//     Function: BillboardEffect::make_default_impl
 //       Access: Protected, Virtual
-//  Description: Intended to be overridden by derived BillboardAttrib
+//  Description: Intended to be overridden by derived BillboardEffect
 //               types to specify what the default property for a
-//               BillboardAttrib of this type should be.
+//               BillboardEffect of this type should be.
 //
-//               This should return a newly-allocated BillboardAttrib of
+//               This should return a newly-allocated BillboardEffect of
 //               the same type that corresponds to whatever the
-//               standard default for this kind of BillboardAttrib is.
+//               standard default for this kind of BillboardEffect is.
 ////////////////////////////////////////////////////////////////////
-RenderAttrib *BillboardAttrib::
+RenderEffect *BillboardEffect::
 make_default_impl() const {
-  return new BillboardAttrib;
+  return new BillboardEffect;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::register_with_read_factory
+//     Function: BillboardEffect::register_with_read_factory
 //       Access: Public, Static
 //  Description: Tells the BamReader how to create objects of type
-//               BillboardAttrib.
+//               BillboardEffect.
 ////////////////////////////////////////////////////////////////////
-void BillboardAttrib::
+void BillboardEffect::
 register_with_read_factory() {
   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::write_datagram
+//     Function: BillboardEffect::write_datagram
 //       Access: Public, Virtual
 //  Description: Writes the contents of this object to the datagram
 //               for shipping out to a Bam file.
 ////////////////////////////////////////////////////////////////////
-void BillboardAttrib::
+void BillboardEffect::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  RenderAttrib::write_datagram(manager, dg);
+  RenderEffect::write_datagram(manager, dg);
 
   dg.add_bool(_off);
   _up_vector.write_datagram(dg);
@@ -233,35 +245,35 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::make_from_bam
+//     Function: BillboardEffect::make_from_bam
 //       Access: Protected, Static
 //  Description: This function is called by the BamReader's factory
-//               when a new object of type BillboardAttrib is encountered
-//               in the Bam file.  It should create the BillboardAttrib
+//               when a new object of type BillboardEffect is encountered
+//               in the Bam file.  It should create the BillboardEffect
 //               and extract its information from the file.
 ////////////////////////////////////////////////////////////////////
-TypedWritable *BillboardAttrib::
+TypedWritable *BillboardEffect::
 make_from_bam(const FactoryParams &params) {
-  BillboardAttrib *attrib = new BillboardAttrib;
+  BillboardEffect *effect = new BillboardEffect;
   DatagramIterator scan;
   BamReader *manager;
 
   parse_params(params, scan, manager);
-  attrib->fillin(scan, manager);
+  effect->fillin(scan, manager);
 
-  return attrib;
+  return effect;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BillboardAttrib::fillin
+//     Function: BillboardEffect::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 BillboardAttrib.
+//               the new BillboardEffect.
 ////////////////////////////////////////////////////////////////////
-void BillboardAttrib::
+void BillboardEffect::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  RenderAttrib::fillin(scan, manager);
+  RenderEffect::fillin(scan, manager);
 
   _off = scan.get_bool();
   _up_vector.read_datagram(scan);

+ 19 - 18
panda/src/pgraph/billboardAttrib.h → panda/src/pgraph/billboardEffect.h

@@ -1,5 +1,5 @@
-// Filename: billboardAttrib.h
-// Created by:  drose (27Feb02)
+// Filename: billboardEffect.h
+// Created by:  drose (14Mar02)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -16,35 +16,35 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#ifndef BILLBOARDATTRIB_H
-#define BILLBOARDATTRIB_H
+#ifndef BILLBOARDEFFECT_H
+#define BILLBOARDEFFECT_H
 
 #include "pandabase.h"
 
-#include "renderAttrib.h"
+#include "renderEffect.h"
 #include "luse.h"
 #include "qpnodePath.h"
 
 ////////////////////////////////////////////////////////////////////
-//       Class : BillboardAttrib
+//       Class : BillboardEffect
 // Description : Indicates that geometry at this node should
 //               automatically rotate to face the camera, or any other
 //               arbitrary node.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA BillboardAttrib : public RenderAttrib {
+class EXPCL_PANDA BillboardEffect : public RenderEffect {
 private:
-  INLINE BillboardAttrib();
+  INLINE BillboardEffect();
 
 PUBLISHED:
-  static CPT(RenderAttrib) make(const LVector3f &up_vector,
+  static CPT(RenderEffect) make(const LVector3f &up_vector,
                                 bool eye_relative,
                                 bool axial_rotate,
                                 float offset,
                                 const qpNodePath &look_at,
                                 const LPoint3f &look_at_point);
-  INLINE static CPT(RenderAttrib) make_axis();
-  INLINE static CPT(RenderAttrib) make_point_eye();
-  INLINE static CPT(RenderAttrib) make_point_world();
+  INLINE static CPT(RenderEffect) make_axis();
+  INLINE static CPT(RenderEffect) make_point_eye();
+  INLINE static CPT(RenderEffect) make_point_world();
 
   INLINE bool is_off() const;
   INLINE const LVector3f &get_up_vector() const;
@@ -55,14 +55,15 @@ PUBLISHED:
   INLINE const LPoint3f &get_look_at_point() const;
 
 public:
+  virtual bool safe_to_combine() const;
   virtual void output(ostream &out) const;
 
   CPT(TransformState) do_billboard(const TransformState *net_transform,
                                    const TransformState *camera_transform) const;
 
 protected:
-  virtual int compare_to_impl(const RenderAttrib *other) const;
-  virtual RenderAttrib *make_default_impl() const;
+  virtual int compare_to_impl(const RenderEffect *other) const;
+  virtual RenderEffect *make_default_impl() const;
 
 private:
   bool _off;
@@ -86,9 +87,9 @@ public:
     return _type_handle;
   }
   static void init_type() {
-    RenderAttrib::init_type();
-    register_type(_type_handle, "BillboardAttrib",
-                  RenderAttrib::get_class_type());
+    RenderEffect::init_type();
+    register_type(_type_handle, "BillboardEffect",
+                  RenderEffect::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();
@@ -99,7 +100,7 @@ private:
   static TypeHandle _type_handle;
 };
 
-#include "billboardAttrib.I"
+#include "billboardEffect.I"
 
 #endif
 

+ 11 - 6
panda/src/pgraph/config_pgraph.cxx

@@ -18,7 +18,7 @@
 
 #include "config_pgraph.h"
 
-#include "billboardAttrib.h"
+#include "billboardEffect.h"
 #include "qpcamera.h"
 #include "colorAttrib.h"
 #include "colorWriteAttrib.h"
@@ -29,7 +29,7 @@
 #include "cullBinUnsorted.h"
 #include "qpcullTraverser.h"
 #include "cullableObject.h"
-#include "decalAttrib.h"
+#include "decalEffect.h"
 #include "depthTestAttrib.h"
 #include "depthWriteAttrib.h"
 #include "qpgeomNode.h"
@@ -40,6 +40,8 @@
 #include "qpnodePathComponent.h"
 #include "pandaNode.h"
 #include "renderAttrib.h"
+#include "renderEffect.h"
+#include "renderEffects.h"
 #include "renderState.h"
 #include "selectiveChildNode.h"
 #include "qpsequenceNode.h"
@@ -79,7 +81,7 @@ init_libpgraph() {
   }
   initialized = true;
 
-  BillboardAttrib::init_type();
+  BillboardEffect::init_type();
   qpCamera::init_type();
   ColorAttrib::init_type();
   ColorWriteAttrib::init_type();
@@ -90,7 +92,7 @@ init_libpgraph() {
   CullBinUnsorted::init_type();
   qpCullTraverser::init_type();
   CullableObject::init_type();
-  DecalAttrib::init_type();
+  DecalEffect::init_type();
   DepthTestAttrib::init_type();
   DepthWriteAttrib::init_type();
   qpGeomNode::init_type();
@@ -101,6 +103,8 @@ init_libpgraph() {
   qpNodePathComponent::init_type();
   PandaNode::init_type();
   RenderAttrib::init_type();
+  RenderEffect::init_type();
+  RenderEffects::init_type();
   RenderState::init_type();
   SelectiveChildNode::init_type();
   qpSequenceNode::init_type();
@@ -109,13 +113,13 @@ init_libpgraph() {
   TransformState::init_type();
   TransparencyAttrib::init_type();
 
-  BillboardAttrib::register_with_read_factory();
+  BillboardEffect::register_with_read_factory();
   qpCamera::register_with_read_factory();
   ColorAttrib::register_with_read_factory();
   ColorWriteAttrib::register_with_read_factory();
   CullBinAttrib::register_with_read_factory();
   CullFaceAttrib::register_with_read_factory();
-  DecalAttrib::register_with_read_factory();
+  DecalEffect::register_with_read_factory();
   DepthTestAttrib::register_with_read_factory();
   DepthWriteAttrib::register_with_read_factory();
   qpGeomNode::register_with_read_factory();
@@ -123,6 +127,7 @@ init_libpgraph() {
   qpLODNode::register_with_read_factory();
   MaterialAttrib::register_with_read_factory();
   PandaNode::register_with_read_factory();
+  RenderEffects::register_with_read_factory();
   RenderState::register_with_read_factory();
   qpSequenceNode::register_with_read_factory();
   TextureApplyAttrib::register_with_read_factory();

+ 5 - 4
panda/src/pgraph/cullTraverserData.cxx

@@ -22,6 +22,7 @@
 #include "pandaNode.h"
 #include "colorAttrib.h"
 #include "textureAttrib.h"
+#include "billboardEffect.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::apply_transform_and_state
@@ -67,11 +68,11 @@ apply_transform_and_state(qpCullTraverser *trav, PandaNode *node) {
     }
   }
 
-  const RenderState *node_state = node->get_state();
-  _state = _state->compose(node_state);
+  _state = _state->compose(node->get_state());
 
-  const BillboardAttrib *billboard = node_state->get_billboard();
-  if (billboard != (const BillboardAttrib *)NULL) {
+  const RenderEffects *node_effects = node->get_effects();
+  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());

+ 7 - 7
panda/src/pgraph/decalAttrib.I → panda/src/pgraph/decalEffect.I

@@ -1,5 +1,5 @@
-// Filename: decalAttrib.I
-// Created by:  drose (04Mar02)
+// Filename: decalEffect.I
+// Created by:  drose (14Mar02)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -18,11 +18,11 @@
 
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DecalAttrib::Constructor
+//     Function: DecalEffect::Constructor
 //       Access: Private
-//  Description: Use DecalAttrib::make() to construct a new
-//               DecalAttrib object.
+//  Description: Use DecalEffect::make() to construct a new
+//               DecalEffect object.
 ////////////////////////////////////////////////////////////////////
-INLINE DecalAttrib::
-DecalAttrib() {
+INLINE DecalEffect::
+DecalEffect() {
 }

+ 41 - 41
panda/src/pgraph/decalAttrib.cxx → panda/src/pgraph/decalEffect.cxx

@@ -1,5 +1,5 @@
-// Filename: decalAttrib.cxx
-// Created by:  drose (04Mar02)
+// Filename: decalEffect.cxx
+// Created by:  drose (14Mar02)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -16,113 +16,113 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#include "decalAttrib.h"
+#include "decalEffect.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "datagram.h"
 #include "datagramIterator.h"
 
-TypeHandle DecalAttrib::_type_handle;
+TypeHandle DecalEffect::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DecalAttrib::make
+//     Function: DecalEffect::make
 //       Access: Published, Static
-//  Description: Constructs a new DecalAttrib object.
+//  Description: Constructs a new DecalEffect object.
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) DecalAttrib::
+CPT(RenderEffect) DecalEffect::
 make() {
-  DecalAttrib *attrib = new DecalAttrib;
-  return return_new(attrib);
+  DecalEffect *effect = new DecalEffect;
+  return return_new(effect);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DecalAttrib::compare_to_impl
+//     Function: DecalEffect::compare_to_impl
 //       Access: Protected, Virtual
-//  Description: Intended to be overridden by derived DecalAttrib
+//  Description: Intended to be overridden by derived DecalEffect
 //               types to return a unique number indicating whether
-//               this DecalAttrib is equivalent to the other one.
+//               this DecalEffect is equivalent to the other one.
 //
-//               This should return 0 if the two DecalAttrib objects
+//               This should return 0 if the two DecalEffect objects
 //               are equivalent, a number less than zero if this one
 //               should be sorted before the other one, and a number
 //               greater than zero otherwise.
 //
-//               This will only be called with two DecalAttrib
+//               This will only be called with two DecalEffect
 //               objects whose get_type() functions return the same.
 ////////////////////////////////////////////////////////////////////
-int DecalAttrib::
-compare_to_impl(const RenderAttrib *other) const {
-  // All DecalAttribs are equivalent--there are no properties to
+int DecalEffect::
+compare_to_impl(const RenderEffect *other) const {
+  // All DecalEffects are equivalent--there are no properties to
   // store.
   return 0;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DecalAttrib::make_default_impl
+//     Function: DecalEffect::make_default_impl
 //       Access: Protected, Virtual
-//  Description: Intended to be overridden by derived DecalAttrib
+//  Description: Intended to be overridden by derived DecalEffect
 //               types to specify what the default property for a
-//               DecalAttrib of this type should be.
+//               DecalEffect of this type should be.
 //
-//               This should return a newly-allocated DecalAttrib of
+//               This should return a newly-allocated DecalEffect of
 //               the same type that corresponds to whatever the
-//               standard default for this kind of DecalAttrib is.
+//               standard default for this kind of DecalEffect is.
 ////////////////////////////////////////////////////////////////////
-RenderAttrib *DecalAttrib::
+RenderEffect *DecalEffect::
 make_default_impl() const {
-  return new DecalAttrib;
+  return new DecalEffect;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DecalAttrib::register_with_read_factory
+//     Function: DecalEffect::register_with_read_factory
 //       Access: Public, Static
 //  Description: Tells the BamReader how to create objects of type
-//               DecalAttrib.
+//               DecalEffect.
 ////////////////////////////////////////////////////////////////////
-void DecalAttrib::
+void DecalEffect::
 register_with_read_factory() {
   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DecalAttrib::write_datagram
+//     Function: DecalEffect::write_datagram
 //       Access: Public, Virtual
 //  Description: Writes the contents of this object to the datagram
 //               for shipping out to a Bam file.
 ////////////////////////////////////////////////////////////////////
-void DecalAttrib::
+void DecalEffect::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  RenderAttrib::write_datagram(manager, dg);
+  RenderEffect::write_datagram(manager, dg);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DecalAttrib::make_from_bam
+//     Function: DecalEffect::make_from_bam
 //       Access: Protected, Static
 //  Description: This function is called by the BamReader's factory
-//               when a new object of type DecalAttrib is encountered
-//               in the Bam file.  It should create the DecalAttrib
+//               when a new object of type DecalEffect is encountered
+//               in the Bam file.  It should create the DecalEffect
 //               and extract its information from the file.
 ////////////////////////////////////////////////////////////////////
-TypedWritable *DecalAttrib::
+TypedWritable *DecalEffect::
 make_from_bam(const FactoryParams &params) {
-  DecalAttrib *attrib = new DecalAttrib;
+  DecalEffect *effect = new DecalEffect;
   DatagramIterator scan;
   BamReader *manager;
 
   parse_params(params, scan, manager);
-  attrib->fillin(scan, manager);
+  effect->fillin(scan, manager);
 
-  return attrib;
+  return effect;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DecalAttrib::fillin
+//     Function: DecalEffect::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 DecalAttrib.
+//               the new DecalEffect.
 ////////////////////////////////////////////////////////////////////
-void DecalAttrib::
+void DecalEffect::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  RenderAttrib::fillin(scan, manager);
+  RenderEffect::fillin(scan, manager);
 }

+ 15 - 15
panda/src/pgraph/decalAttrib.h → panda/src/pgraph/decalEffect.h

@@ -1,5 +1,5 @@
-// Filename: decalAttrib.h
-// Created by:  drose (04Mar02)
+// Filename: decalEffect.h
+// Created by:  drose (14Mar02)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -16,29 +16,29 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#ifndef DECALATTRIB_H
-#define DECALATTRIB_H
+#ifndef DECALEFFECT_H
+#define DECALEFFECT_H
 
 #include "pandabase.h"
 
-#include "renderAttrib.h"
+#include "renderEffect.h"
 
 ////////////////////////////////////////////////////////////////////
-//       Class : DecalAttrib
+//       Class : DecalEffect
 // Description : Applied to a GeomNode to indicate that the children
 //               of this GeomNode are coplanar and should be drawn as
 //               decals (eliminating Z-fighting).
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA DecalAttrib : public RenderAttrib {
+class EXPCL_PANDA DecalEffect : public RenderEffect {
 private:
-  INLINE DecalAttrib();
+  INLINE DecalEffect();
 
 PUBLISHED:
-  static CPT(RenderAttrib) make();
+  static CPT(RenderEffect) make();
 
 protected:
-  virtual int compare_to_impl(const RenderAttrib *other) const;
-  virtual RenderAttrib *make_default_impl() const;
+  virtual int compare_to_impl(const RenderEffect *other) const;
+  virtual RenderEffect *make_default_impl() const;
 
 public:
   static void register_with_read_factory();
@@ -53,9 +53,9 @@ public:
     return _type_handle;
   }
   static void init_type() {
-    RenderAttrib::init_type();
-    register_type(_type_handle, "DecalAttrib",
-                  RenderAttrib::get_class_type());
+    RenderEffect::init_type();
+    register_type(_type_handle, "DecalEffect",
+                  RenderEffect::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();
@@ -66,7 +66,7 @@ private:
   static TypeHandle _type_handle;
 };
 
-#include "decalAttrib.I"
+#include "decalEffect.I"
 
 #endif
 

+ 96 - 4
panda/src/pgraph/pandaNode.I

@@ -106,6 +106,7 @@ get_parent() const {
 INLINE PandaNode::CData::
 CData() {
   _state = RenderState::make_empty();
+  _effects = RenderEffects::make_empty();
   _transform = TransformState::make_identity();
   _draw_mask = DrawMask::all_on();
 }
@@ -122,6 +123,7 @@ CData(const PandaNode::CData &copy) :
   _up(copy._up),
   _paths(copy._paths),
   _state(copy._state),
+  _effects(copy._effects),
   _transform(copy._transform),
   _draw_mask(copy._draw_mask)
 {
@@ -409,6 +411,61 @@ clear_attrib(TypeHandle type) {
   cdata->_state = cdata->_state->remove_attrib(type);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::set_effect
+//       Access: Published
+//  Description: Adds the indicated render effect to the scene
+//               graph on this node.  If there was already an effect
+//               of the same type, it is replaced.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+set_effect(const RenderEffect *effect) {
+  CDWriter cdata(_cycler);
+  cdata->_effects = cdata->_effects->add_effect(effect);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_effect
+//       Access: Published
+//  Description: Returns the render effect of the indicated type,
+//               if it is defined on the node, or NULL if it is not.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderEffect *PandaNode::
+get_effect(TypeHandle type) const {
+  CDReader cdata(_cycler);
+  int index = cdata->_effects->find_effect(type);
+  if (index >= 0) {
+    return cdata->_effects->get_effect(index);
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::has_effect
+//       Access: Published
+//  Description: Returns true if there is a render effect of the
+//               indicated type defined on this node, or false if
+//               there is not.
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaNode::
+has_effect(TypeHandle type) const {
+  CDReader cdata(_cycler);
+  int index = cdata->_effects->find_effect(type);
+  return (index >= 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::clear_effect
+//       Access: Published
+//  Description: Removes the render effect of the given type from
+//               this node.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+clear_effect(TypeHandle type) {
+  CDWriter cdata(_cycler);
+  cdata->_effects = cdata->_effects->remove_effect(type);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::set_state
 //       Access: Published
@@ -454,6 +511,43 @@ clear_state() {
   cdata->_state = RenderState::make_empty();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::set_effects
+//       Access: Published
+//  Description: Sets the complete RenderEffects that will be applied
+//               this node.  This completely replaces whatever has
+//               been set on this node via repeated calls to
+//               set_attrib().
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+set_effects(const RenderEffects *effects) {
+  CDWriter cdata(_cycler);
+  cdata->_effects = effects;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_effects
+//       Access: Published
+//  Description: Returns the complete RenderEffects that will be
+//               applied to this node.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderEffects *PandaNode::
+get_effects() const {
+  CDReader cdata(_cycler);
+  return cdata->_effects;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::clear_effects
+//       Access: Published
+//  Description: Resets this node to have no render effects.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+clear_effects() {
+  CDWriter cdata(_cycler);
+  cdata->_effects = RenderEffects::make_empty();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::set_transform
 //       Access: Published
@@ -463,10 +557,8 @@ clear_state() {
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 set_transform(const TransformState *transform) {
-  {
-    CDWriter cdata(_cycler);
-    cdata->_transform = transform;
-  }
+  CDWriter cdata(_cycler);
+  cdata->_transform = transform;
   mark_bound_stale();
 }
 

+ 14 - 4
panda/src/pgraph/pandaNode.cxx

@@ -47,6 +47,7 @@ make_copy() const {
 void PandaNode::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
   manager->write_pointer(dg, _state);
+  manager->write_pointer(dg, _effects);
   manager->write_pointer(dg, _transform);
 
   dg.add_uint32(_draw_mask.get_word());
@@ -67,10 +68,12 @@ int PandaNode::CData::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = CycleData::complete_pointers(p_list, manager);
 
-  // Get the state and transform pointers.
+  // Get the state, effects, and transform pointers.
   _state = DCAST(RenderState, p_list[pi++]);
+  _effects = DCAST(RenderEffects, p_list[pi++]);
   _transform = DCAST(TransformState, p_list[pi++]);
 
+  // Get the parent and child pointers.
   pi += complete_up_list(_up, p_list + pi, manager);
   pi += complete_down_list(_down, p_list + pi, manager);
   pi += complete_down_list(_stashed, p_list + pi, manager);
@@ -87,12 +90,14 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void PandaNode::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  // Read the state and transform pointers.
+  // Read the state, effects, and transform pointers.
+  manager->read_pointer(scan);
   manager->read_pointer(scan);
   manager->read_pointer(scan);
 
   _draw_mask.set_word(scan.get_uint32());
 
+  // Read the parent and child pointers.
   fillin_up_list(_up, scan, manager);
   fillin_down_list(_down, scan, manager);
   fillin_down_list(_stashed, scan, manager);
@@ -304,6 +309,7 @@ PandaNode(const PandaNode &copy) :
   CDReader copy_cdata(copy._cycler);
   CDWriter cdata(_cycler);
   cdata->_state = copy_cdata->_state;
+  cdata->_effects = copy_cdata->_effects;
   cdata->_transform = copy_cdata->_transform;
 }
 
@@ -989,6 +995,9 @@ write(ostream &out, int indent_level) const {
   if (!cdata->_state->is_empty()) {
     out << " " << *cdata->_state;
   }
+  if (!cdata->_effects->is_empty()) {
+    out << " " << *cdata->_effects;
+  }
   out << "\n";
 }
 
@@ -1159,8 +1168,6 @@ r_copy_subgraph(PandaNode::InstanceMap &inst_map) const {
   }
 
   copy->r_copy_children(this, inst_map);
-  copy->set_state(get_state());
-  copy->set_transform(get_transform());
   return copy;
 }
 
@@ -1528,6 +1535,9 @@ r_list_descendants(ostream &out, int indent_level) const {
   if (!cdata->_state->is_empty()) {
     out << " " << *cdata->_state;
   }
+  if (!cdata->_effects->is_empty()) {
+    out << " " << *cdata->_effects;
+  }
   out << "\n";
 
   Down::const_iterator di;

+ 11 - 0
panda/src/pgraph/pandaNode.h

@@ -26,6 +26,7 @@
 #include "cycleDataWriter.h"
 #include "pipelineCycler.h"
 #include "renderState.h"
+#include "renderEffects.h"
 #include "transformState.h"
 #include "drawMask.h"
 #include "typedWritable.h"
@@ -106,10 +107,19 @@ PUBLISHED:
   INLINE bool has_attrib(TypeHandle type) const;
   INLINE void clear_attrib(TypeHandle type);
 
+  INLINE void set_effect(const RenderEffect *effect);
+  INLINE const RenderEffect *get_effect(TypeHandle type) const;
+  INLINE bool has_effect(TypeHandle type) const;
+  INLINE void clear_effect(TypeHandle type);
+
   INLINE void set_state(const RenderState *state);
   INLINE const RenderState *get_state() const;
   INLINE void clear_state();
 
+  INLINE void set_effects(const RenderEffects *effects);
+  INLINE const RenderEffects *get_effects() const;
+  INLINE void clear_effects();
+
   INLINE void set_transform(const TransformState *transform);
   INLINE const TransformState *get_transform() const;
   INLINE void clear_transform();
@@ -245,6 +255,7 @@ private:
     Paths _paths;
 
     CPT(RenderState) _state;
+    CPT(RenderEffects) _effects;
     CPT(TransformState) _transform;
     DrawMask _draw_mask;
   };

+ 1 - 1
panda/src/pgraph/pgraph_composite1.cxx

@@ -1,4 +1,4 @@
-#include "billboardAttrib.cxx"
+#include "billboardEffect.cxx"
 #include "binCullHandler.cxx"
 #include "qpcamera.cxx"
 #include "colorAttrib.cxx"

+ 3 - 1
panda/src/pgraph/pgraph_composite2.cxx

@@ -1,4 +1,4 @@
-#include "decalAttrib.cxx"
+#include "decalEffect.cxx"
 #include "depthTestAttrib.cxx"
 #include "depthWriteAttrib.cxx"
 #include "drawCullHandler.cxx"
@@ -14,6 +14,8 @@
 #include "qpnodePathComponent.cxx"
 #include "pandaNode.cxx"
 #include "renderAttrib.cxx"
+#include "renderEffect.cxx"
+#include "renderEffects.cxx"
 #include "renderState.cxx"
 #include "selectiveChildNode.cxx"
 #include "qpsequenceNode.cxx"

+ 3 - 4
panda/src/pgraph/qpcullTraverser.cxx

@@ -20,7 +20,6 @@
 #include "cullTraverserData.h"
 #include "transformState.h"
 #include "renderState.h"
-#include "billboardAttrib.h"
 #include "cullHandler.h"
 #include "dcast.h"
 #include "qpgeomNode.h"
@@ -116,8 +115,8 @@ traverse(PandaNode *node, const CullTraverserData &data) {
 ////////////////////////////////////////////////////////////////////
 void qpCullTraverser::
 traverse_below(PandaNode *node, const CullTraverserData &data) {
-  const RenderState *node_state = node->get_state();
-  if (node_state->has_decal()) {
+  const RenderEffects *node_effects = node->get_effects();
+  if (node_effects->has_decal()) {
     start_decal(node, data);
     
   } else {
@@ -161,7 +160,7 @@ void qpCullTraverser::
 start_decal(PandaNode *node, const CullTraverserData &data) {
   if (!node->is_geom_node()) {
     pgraph_cat.error()
-      << "DecalAttrib applied to " << *node << ", not a GeomNode.\n";
+      << "DecalEffect applied to " << *node << ", not a GeomNode.\n";
     return;
   }
 

+ 11 - 11
panda/src/pgraph/qpnodePath.cxx

@@ -27,7 +27,7 @@
 #include "textureAttrib.h"
 #include "materialAttrib.h"
 #include "cullFaceAttrib.h"
-#include "billboardAttrib.h"
+#include "billboardEffect.h"
 #include "transparencyAttrib.h"
 #include "materialPool.h"
 #include "look_at.h"
@@ -1981,10 +1981,10 @@ do_billboard_point_world(const qpNodePath &camera, float offset) {
 void qpNodePath::
 set_billboard_axis(float offset) {
   nassertv(!is_empty());
-  CPT(RenderAttrib) billboard = BillboardAttrib::make
+  CPT(RenderEffect) billboard = BillboardEffect::make
     (LVector3f::up(), false, true, 
      offset, qpNodePath(), LPoint3f(0.0f, 0.0f, 0.0f));
-  node()->set_attrib(billboard);
+  node()->set_effect(billboard);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1998,10 +1998,10 @@ set_billboard_axis(float offset) {
 void qpNodePath::
 set_billboard_point_eye(float offset) {
   nassertv(!is_empty());
-  CPT(RenderAttrib) billboard = BillboardAttrib::make
+  CPT(RenderEffect) billboard = BillboardEffect::make
     (LVector3f::up(), true, false,
      offset, qpNodePath(), LPoint3f(0.0f, 0.0f, 0.0f));
-  node()->set_attrib(billboard);
+  node()->set_effect(billboard);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2014,33 +2014,33 @@ set_billboard_point_eye(float offset) {
 void qpNodePath::
 set_billboard_point_world(float offset) {
   nassertv(!is_empty());
-  CPT(RenderAttrib) billboard = BillboardAttrib::make
+  CPT(RenderEffect) billboard = BillboardEffect::make
     (LVector3f::up(), false, false,
      offset, qpNodePath(), LPoint3f(0.0f, 0.0f, 0.0f));
-  node()->set_attrib(billboard);
+  node()->set_effect(billboard);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpNodePath::clear_billboard
 //       Access: Published
-//  Description: Removes any billboard attributes from the node.
+//  Description: Removes any billboard effect from the node.
 ////////////////////////////////////////////////////////////////////
 void qpNodePath::
 clear_billboard() {
   nassertv(!is_empty());
-  node()->clear_attrib(BillboardAttrib::get_class_type());
+  node()->clear_effect(BillboardEffect::get_class_type());
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpNodePath::has_billboard
 //       Access: Published
-//  Description: Returns true if there is any billboard attribute on
+//  Description: Returns true if there is any billboard effect on
 //               the node.
 ////////////////////////////////////////////////////////////////////
 bool qpNodePath::
 has_billboard() const {
   nassertr(!is_empty(), false);
-  return node()->has_attrib(BillboardAttrib::get_class_type());
+  return node()->has_effect(BillboardEffect::get_class_type());
 }
 
 ////////////////////////////////////////////////////////////////////

+ 10 - 0
panda/src/pgraph/renderAttrib.h

@@ -36,6 +36,16 @@ class GraphicsStateGuardianBase;
 //               geometry.  This includes TextureAttrib, ColorAttrib,
 //               etc.
 //
+//               RenderAttrib represents render attributes that always
+//               propagate down to the leaves without regard to the
+//               particular node they are assigned to.  A RenderAttrib
+//               will have the same effect on a leaf node whether it
+//               is assigned to the graph at the leaf or several nodes
+//               above.  This is different from RenderEffect, which
+//               represents a particular render property that is
+//               applied immediately to the node on which it is
+//               encountered, like billboarding or decaling.
+//
 //               You should not attempt to create or modify a
 //               RenderAttrib directly; instead, use the make() method
 //               of the appropriate kind of attrib you want.  This

+ 44 - 0
panda/src/pgraph/renderEffect.I

@@ -0,0 +1,44 @@
+// Filename: renderEffect.I
+// Created by:  drose (14Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::compare_to
+//       Access: Public
+//  Description: Provides an arbitrary ordering among all unique
+//               RenderEffects, 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 RenderEffect
+//               class because all equivalent RenderEffect objects are
+//               guaranteed to share the same pointer; thus, a pointer
+//               comparison is always sufficient.
+////////////////////////////////////////////////////////////////////
+INLINE int RenderEffect::
+compare_to(const RenderEffect &other) const {
+  // First, we compare the types; if they are of different types then
+  // they sort differently.
+  TypeHandle type = get_type();
+  TypeHandle other_type = other.get_type();
+  if (type != other_type) {
+    return type.get_index() - other_type.get_index();
+  }
+
+  // We only call compare_to_impl() if they have the same type.
+  return compare_to_impl(&other);
+}

+ 257 - 0
panda/src/pgraph/renderEffect.cxx

@@ -0,0 +1,257 @@
+// Filename: renderEffect.cxx
+// Created by:  drose (14Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "renderEffect.h"
+#include "bamReader.h"
+#include "indent.h"
+
+RenderEffect::Effects RenderEffect::_effects;
+TypeHandle RenderEffect::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+RenderEffect::
+RenderEffect() {
+  _saved_entry = _effects.end();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::Copy Constructor
+//       Access: Private
+//  Description: RenderEffects are not meant to be copied.
+////////////////////////////////////////////////////////////////////
+RenderEffect::
+RenderEffect(const RenderEffect &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::Copy Assignment Operator
+//       Access: Private
+//  Description: RenderEffects are not meant to be copied.
+////////////////////////////////////////////////////////////////////
+void RenderEffect::
+operator = (const RenderEffect &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::Destructor
+//       Access: Public, Virtual
+//  Description: The destructor is responsible for removing the
+//               RenderEffect from the global set if it is there.
+////////////////////////////////////////////////////////////////////
+RenderEffect::
+~RenderEffect() {
+  if (_saved_entry != _effects.end()) {
+    _effects.erase(_saved_entry);
+    _saved_entry = _effects.end();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::safe_to_transform
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to transform
+//               this particular kind of RenderEffect by calling the
+//               xform() method, false otherwise.  For instance, it's
+//               usually a bad idea to attempt to xform a Character.
+////////////////////////////////////////////////////////////////////
+bool RenderEffect::
+safe_to_transform() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::safe_to_combine
+//       Access: Public, Virtual
+//  Description: Returns true if this kind of effect can safely be
+//               combined with sibling nodes that share the exact same
+//               effect, or false if this is not a good idea.
+////////////////////////////////////////////////////////////////////
+bool RenderEffect::
+safe_to_combine() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::xform
+//       Access: Public, Virtual
+//  Description: Returns a new RenderEffect transformed by the
+//               indicated matrix.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffect) RenderEffect::
+xform(const LMatrix4f &) const {
+  return this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void RenderEffect::
+output(ostream &out) const {
+  out << get_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void RenderEffect::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::return_new
+//       Access: Protected, Static
+//  Description: This function is used by derived RenderEffect types
+//               to share a common RenderEffect pointer for all
+//               equivalent RenderEffect objects.
+//
+//               The make() function of the derived type should create
+//               a new RenderEffect and pass it through return_new(),
+//               which will either save the pointer and return it
+//               unchanged (if this is the first similar such object)
+//               or delete it and return an equivalent pointer (if
+//               there was already a similar object saved).
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffect) RenderEffect::
+return_new(RenderEffect *effect) {
+  nassertr(effect != (RenderEffect *)NULL, effect);
+
+  // This should be a newly allocated pointer, not one that was used
+  // for anything else.
+  nassertr(effect->_saved_entry == _effects.end(), effect);
+
+  // Save the effect in a local PointerTo so that it will be freed at
+  // the end of this function if no one else uses it.
+  CPT(RenderEffect) pt_effect = effect;
+
+  pair<Effects::iterator, bool> result = _effects.insert(effect);
+  if (result.second) {
+    // The effect was inserted; save the iterator and return the
+    // input effect.
+    effect->_saved_entry = result.first;
+    return pt_effect;
+  }
+
+  // The effect was not inserted; there must be an equivalent one
+  // already in the set.  Return that one.
+  return *(result.first);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderEffect
+//               types to return a unique number indicating whether
+//               this RenderEffect is equivalent to the other one.
+//
+//               This should return 0 if the two RenderEffect objects
+//               are equivalent, a number less than zero if this one
+//               should be sorted before the other one, and a number
+//               greater than zero otherwise.
+//
+//               This will only be called with two RenderEffect
+//               objects whose get_type() functions return the same.
+////////////////////////////////////////////////////////////////////
+int RenderEffect::
+compare_to_impl(const RenderEffect *other) const {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void RenderEffect::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::change_this
+//       Access: Public, Static
+//  Description: Called immediately after complete_pointers(), this
+//               gives the object a chance to adjust its own pointer
+//               if desired.  Most objects don't change pointers after
+//               completion, but some need to.
+//
+//               Once this function has been called, the old pointer
+//               will no longer be accessed.
+////////////////////////////////////////////////////////////////////
+TypedWritable *RenderEffect::
+change_this(TypedWritable *old_ptr, BamReader *manager) {
+  // First, uniquify the pointer.
+  RenderEffect *effect = DCAST(RenderEffect, old_ptr);
+  CPT(RenderEffect) pointer = return_new(effect);
+
+  // 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 == effect) {
+    pointer->ref();
+    manager->register_finalize(effect);
+  }
+  
+  // We have to cast the pointer back to non-const, because the bam
+  // reader expects that.
+  return (RenderEffect *)pointer.p();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffect::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 RenderEffect::
+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: RenderEffect::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 RenderEffect.
+////////////////////////////////////////////////////////////////////
+void RenderEffect::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+}

+ 120 - 0
panda/src/pgraph/renderEffect.h

@@ -0,0 +1,120 @@
+// Filename: renderEffect.h
+// Created by:  drose (14Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef RENDEREFFECT_H
+#define RENDEREFFECT_H
+
+#include "pandabase.h"
+
+#include "typedWritableReferenceCount.h"
+#include "indirectCompareTo.h"
+#include "pointerTo.h"
+#include "pset.h"
+#include "luse.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : RenderEffect
+// Description : This is the base class for a number of special render
+//               effects that may be set on scene graph nodes to
+//               change the way they render.  This includes
+//               BillboardEffect, DecalEffect, etc.
+//
+//               RenderEffect represents render properties that must
+//               be applied as soon as they are encountered in the
+//               scene graph, rather than propagating down to the
+//               leaves.  This is different from RenderAttrib, which
+//               represents properties like color and texture that
+//               don't do anything until they propagate down to a
+//               GeomNode.
+//
+//               You should not attempt to create or modify a
+//               RenderEffect directly; instead, use the make() method
+//               of the appropriate kind of effect you want.  This
+//               will allocate and return a new RenderEffect of the
+//               appropriate type, and it may share pointers if
+//               possible.  Do not modify the new RenderEffect if you
+//               wish to change its properties; instead, create a new
+//               one.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA RenderEffect : public TypedWritableReferenceCount {
+protected:
+  RenderEffect();
+private:
+  RenderEffect(const RenderEffect &copy);
+  void operator = (const RenderEffect &copy);
+
+public:
+  virtual ~RenderEffect();
+
+  INLINE int compare_to(const RenderEffect &other) const;
+
+  virtual bool safe_to_transform() const;
+  virtual bool safe_to_combine() const;
+  virtual CPT(RenderEffect) xform(const LMatrix4f &mat) const;
+
+PUBLISHED:
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+protected:
+  static CPT(RenderEffect) return_new(RenderEffect *effect);
+
+  virtual int compare_to_impl(const RenderEffect *other) const;
+
+private:
+  typedef pset<const RenderEffect *, IndirectCompareTo<RenderEffect> > Effects;
+  static Effects _effects;
+
+  Effects::iterator _saved_entry;
+
+public:
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
+  virtual void finalize();
+
+protected:
+  static TypedWritable *new_from_bam(RenderEffect *effect, BamReader *manager);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "RenderEffect",
+                  TypedWritableReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+INLINE ostream &operator << (ostream &out, const RenderEffect &effect) {
+  effect.output(out);
+  return out;
+}
+
+#include "renderEffect.I"
+
+#endif
+

+ 182 - 0
panda/src/pgraph/renderEffects.I

@@ -0,0 +1,182 @@
+// Filename: renderEffects.I
+// Created by:  drose (14Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Effect::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE RenderEffects::Effect::
+Effect(const RenderEffect *effect) :
+  _type(effect->get_type()),
+  _effect(effect)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Effect::Constructor
+//       Access: Public
+//  Description: This constructor is only used when reading the
+//               RenderEffects from a bam file.  At this point, the
+//               effect pointer is unknown.
+////////////////////////////////////////////////////////////////////
+INLINE RenderEffects::Effect::
+Effect() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Effect::Constructor
+//       Access: Public
+//  Description: This constructor makes an invalid Effect with no
+//               RenderEffect pointer; its purpose is just to make an
+//               object we can use to look up a particular type in the
+//               Effect set.
+////////////////////////////////////////////////////////////////////
+INLINE RenderEffects::Effect::
+Effect(TypeHandle type) :
+  _type(type),
+  _effect(NULL)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Effect::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE RenderEffects::Effect::
+Effect(const Effect &copy) :
+  _type(copy._type),
+  _effect(copy._effect)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Effect::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void RenderEffects::Effect::
+operator = (const Effect &copy) {
+  _type = copy._type;
+  _effect = copy._effect;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Effect::operator <
+//       Access: Public
+//  Description: This is used by the Effects set to uniquify
+//               RenderEffects by type.  Only one RenderEffect of a
+//               given type is allowed in the set.  This ordering must
+//               also match the ordering reported by compare_to().
+////////////////////////////////////////////////////////////////////
+INLINE bool RenderEffects::Effect::
+operator < (const Effect &other) const {
+  return _type < other._type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Effect::compare_to
+//       Access: Public
+//  Description: Provides an indication of whether a particular
+//               effect is equivalent to another one, for purposes
+//               of generating unique RenderEffectss.  This should
+//               compare all properties of the Effect, but it is
+//               important that the type is compared first, to be
+//               consistent with the ordering defined by operator <.
+////////////////////////////////////////////////////////////////////
+INLINE int RenderEffects::Effect::
+compare_to(const Effect &other) const {
+  if (_type != other._type) {
+    return _type.get_index() - other._type.get_index();
+  }
+  return _effect - other._effect;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::is_empty
+//       Access: Published
+//  Description: Returns true if the state is empty, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool RenderEffects::
+is_empty() const {
+  return _effects.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::get_num_effects
+//       Access: Published
+//  Description: Returns the number of separate effects indicated
+//               in the state.
+////////////////////////////////////////////////////////////////////
+INLINE int RenderEffects::
+get_num_effects() const {
+  return _effects.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::get_effect
+//       Access: Published
+//  Description: Returns the nth effect in the state.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderEffect *RenderEffects::
+get_effect(int n) const {
+  nassertr(n >= 0 && n < (int)_effects.size(), NULL);
+  return _effects[n]._effect;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::get_billboard
+//       Access: Public
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a BillboardEffect on this state.  It returns a
+//               pointer to the BillboardEffect, if there is one, or
+//               NULL if there is not.
+////////////////////////////////////////////////////////////////////
+INLINE const BillboardEffect *RenderEffects::
+get_billboard() const {
+  if ((_flags & F_checked_billboard) == 0) {
+    // We pretend this function is const, even though it transparently
+    // modifies the internal billboard cache.
+    ((RenderEffects *)this)->determine_billboard();
+  }
+  return _billboard;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::has_decal
+//       Access: Public
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a DecalEffect on this state.  It returns true if a
+//               DecalEffect exists, false otherwise.  Note that since
+//               there is no additional information stored on the
+//               DecalEffect, there's no point in returning it if it
+//               exists.
+////////////////////////////////////////////////////////////////////
+INLINE bool RenderEffects::
+has_decal() const {
+  if ((_flags & F_checked_decal) == 0) {
+    // We pretend this function is const, even though it transparently
+    // modifies the internal decal cache.
+    ((RenderEffects *)this)->determine_decal();
+  }
+  return ((_flags & F_has_decal) != 0);
+}

+ 618 - 0
panda/src/pgraph/renderEffects.cxx

@@ -0,0 +1,618 @@
+// Filename: renderEffects.cxx
+// Created by:  drose (14Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "renderEffects.h"
+#include "billboardEffect.h"
+#include "decalEffect.h"
+#include "config_pgraph.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagramIterator.h"
+#include "indent.h"
+#include "compareTo.h"
+
+RenderEffects::States RenderEffects::_states;
+CPT(RenderEffects) RenderEffects::_empty_state;
+TypeHandle RenderEffects::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Constructor
+//       Access: Protected
+//  Description: Actually, this could be a private constructor, since
+//               no one inherits from RenderEffects, but gcc gives us a
+//               spurious warning if all constructors are private.
+////////////////////////////////////////////////////////////////////
+RenderEffects::
+RenderEffects() {
+  _saved_entry = _states.end();
+  _flags = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Copy Constructor
+//       Access: Private
+//  Description: RenderEffectss are not meant to be copied.
+////////////////////////////////////////////////////////////////////
+RenderEffects::
+RenderEffects(const RenderEffects &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Copy Assignment Operator
+//       Access: Private
+//  Description: RenderEffectss are not meant to be copied.
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+operator = (const RenderEffects &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::Destructor
+//       Access: Public, Virtual
+//  Description: The destructor is responsible for removing the
+//               RenderEffects from the global set if it is there.
+////////////////////////////////////////////////////////////////////
+RenderEffects::
+~RenderEffects() {
+  // Remove the deleted RenderEffects object from the global pool.
+  if (_saved_entry != _states.end()) {
+    _states.erase(_saved_entry);
+    _saved_entry = _states.end();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::operator <
+//       Access: Public
+//  Description: Provides an arbitrary ordering among all unique
+//               RenderEffectss, 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 RenderEffects
+//               class because all equivalent RenderEffects objects are
+//               guaranteed to share the same pointer; thus, a pointer
+//               comparison is always sufficient.
+////////////////////////////////////////////////////////////////////
+bool RenderEffects::
+operator < (const RenderEffects &other) const {
+  // We must compare all the properties of the effects, not just
+  // the type; thus, we compare them one at a time using compare_to().
+  return lexicographical_compare(_effects.begin(), _effects.end(),
+                                 other._effects.begin(), other._effects.end(),
+                                 CompareTo<Effect>());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::safe_to_transform
+//       Access: Public
+//  Description: Returns true if all of the effects in this set can
+//               safely be transformed, and therefore the complete set
+//               can be transformed, by calling xform().
+////////////////////////////////////////////////////////////////////
+bool RenderEffects::
+safe_to_transform() const {
+  Effects::const_iterator ai;
+  for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
+    const Effect &effect = (*ai);
+    if (!effect._effect->safe_to_transform()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::safe_to_combine
+//       Access: Public
+//  Description: Returns true if all of the effects in this set can
+//               safely be shared with a sibling node that has the
+//               exact same set of effects, or false if this would be
+//               bad for any of the effects.
+////////////////////////////////////////////////////////////////////
+bool RenderEffects::
+safe_to_combine() const {
+  Effects::const_iterator ai;
+  for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
+    const Effect &effect = (*ai);
+    if (!effect._effect->safe_to_combine()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::xform
+//       Access: Public, Virtual
+//  Description: Returns a new RenderEffects transformed by the
+//               indicated matrix.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffects) RenderEffects::
+xform(const LMatrix4f &mat) const {
+  if (is_empty()) {
+    return this;
+  }
+
+  RenderEffects *new_state = new RenderEffects;
+  back_insert_iterator<Effects> result = 
+    back_inserter(new_state->_effects);
+
+  Effects::const_iterator ai;
+  for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
+    const Effect &effect = (*ai);
+    Effect new_effect(effect);
+    new_effect._effect = effect._effect->xform(mat);
+    *result = new_effect;
+    ++result;
+  }
+
+  return return_new(new_state);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::find_effect
+//       Access: Published
+//  Description: Searches for an effect with the indicated type in
+//               the state, and returns its index if it is found, or
+//               -1 if it is not.
+////////////////////////////////////////////////////////////////////
+int RenderEffects::
+find_effect(TypeHandle type) const {
+  Effects::const_iterator ai = _effects.find(Effect(type));
+  if (ai == _effects.end()) {
+    return -1;
+  }
+  return ai - _effects.begin();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::make_empty
+//       Access: Published, Static
+//  Description: Returns a RenderEffects with no effects set.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffects) RenderEffects::
+make_empty() {
+  // The empty 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 (_empty_state == (RenderEffects *)NULL) {
+    RenderEffects *state = new RenderEffects;
+    _empty_state = return_new(state);
+  }
+
+  return _empty_state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::make
+//       Access: Published, Static
+//  Description: Returns a RenderEffects with one effect set.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffects) RenderEffects::
+make(const RenderEffect *effect) {
+  RenderEffects *state = new RenderEffects;
+  state->_effects.reserve(1);
+  state->_effects.insert(Effect(effect));
+  return return_new(state);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::make
+//       Access: Published, Static
+//  Description: Returns a RenderEffects with two effects set.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffects) RenderEffects::
+make(const RenderEffect *effect1,
+     const RenderEffect *effect2) {
+  RenderEffects *state = new RenderEffects;
+  state->_effects.reserve(2);
+  state->_effects.push_back(Effect(effect1));
+  state->_effects.push_back(Effect(effect2));
+  state->_effects.sort();
+  return return_new(state);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::make
+//       Access: Published, Static
+//  Description: Returns a RenderEffects with three effects set.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffects) RenderEffects::
+make(const RenderEffect *effect1,
+     const RenderEffect *effect2,
+     const RenderEffect *effect3) {
+  RenderEffects *state = new RenderEffects;
+  state->_effects.reserve(2);
+  state->_effects.push_back(Effect(effect1));
+  state->_effects.push_back(Effect(effect2));
+  state->_effects.push_back(Effect(effect3));
+  state->_effects.sort();
+  return return_new(state);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::make
+//       Access: Published, Static
+//  Description: Returns a RenderEffects with four effects set.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffects) RenderEffects::
+make(const RenderEffect *effect1,
+     const RenderEffect *effect2,
+     const RenderEffect *effect3,
+     const RenderEffect *effect4) {
+  RenderEffects *state = new RenderEffects;
+  state->_effects.reserve(2);
+  state->_effects.push_back(Effect(effect1));
+  state->_effects.push_back(Effect(effect2));
+  state->_effects.push_back(Effect(effect3));
+  state->_effects.push_back(Effect(effect4));
+  state->_effects.sort();
+  return return_new(state);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::add_effect
+//       Access: Published
+//  Description: Returns a new RenderEffects object that represents the
+//               same as the source state, with the new RenderEffect
+//               added.  If there is already a RenderEffect with the
+//               same type, it is replaced.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffects) RenderEffects::
+add_effect(const RenderEffect *effect) const {
+  RenderEffects *new_state = new RenderEffects;
+  back_insert_iterator<Effects> result = 
+    back_inserter(new_state->_effects);
+
+  Effect new_effect(effect);
+  Effects::const_iterator ai = _effects.begin();
+
+  while (ai != _effects.end() && (*ai) < new_effect) {
+    *result = *ai;
+    ++ai;
+    ++result;
+  }
+  *result = new_effect;
+  ++result;
+
+  if (ai != _effects.end() && !(new_effect < (*ai))) {
+    // At this point we know:
+    // !((*ai) < new_effect) && !(new_effect < (*ai))
+    // which means (*ai) == new_effect--so we should leave it out,
+    // to avoid duplicating effects in the set.
+    ++ai;
+  }
+
+  while (ai != _effects.end()) {
+    *result = *ai;
+    ++ai;
+    ++result;
+  }
+
+  return return_new(new_state);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::remove_effect
+//       Access: Published
+//  Description: Returns a new RenderEffects object that represents the
+//               same as the source state, with the indicated
+//               RenderEffect removed.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffects) RenderEffects::
+remove_effect(TypeHandle type) const {
+  RenderEffects *new_state = new RenderEffects;
+  back_insert_iterator<Effects> result = 
+    back_inserter(new_state->_effects);
+
+  Effects::const_iterator ai = _effects.begin();
+
+  while (ai != _effects.end()) {
+    if ((*ai)._type != type) {
+      *result = *ai;
+      ++result;
+    }
+    ++ai;
+  }
+
+  return return_new(new_state);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::get_effect
+//       Access: Published, Virtual
+//  Description: Looks for a RenderEffect of the indicated type in the
+//               state, and returns it if it is found, or NULL if it
+//               is not.
+////////////////////////////////////////////////////////////////////
+const RenderEffect *RenderEffects::
+get_effect(TypeHandle type) const {
+  Effects::const_iterator ai;
+  ai = _effects.find(Effect(type));
+  if (ai != _effects.end()) {
+    return (*ai)._effect;
+  }
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+output(ostream &out) const {
+  out << "E:";
+  if (_effects.empty()) {
+    out << "(empty)";
+
+  } else {
+    Effects::const_iterator ai = _effects.begin();
+    out << "(" << (*ai)._type;
+    ++ai;
+    while (ai != _effects.end()) {
+      out << " " << (*ai)._type;
+      ++ai;
+    }
+    out << ")";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << _effects.size() << " effects:\n";
+  Effects::const_iterator ai;
+  for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
+    const Effect &effect = (*ai);
+    effect._effect->write(out, indent_level + 2);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::return_new
+//       Access: Private, Static
+//  Description: This function is used to share a common RenderEffects
+//               pointer for all equivalent RenderEffects objects.
+//
+//               See the similar logic in RenderEffect.  The idea is
+//               to create a new RenderEffects object and pass it
+//               through this function, which will share the pointer
+//               with a previously-created RenderEffects object if it is
+//               equivalent.
+////////////////////////////////////////////////////////////////////
+CPT(RenderEffects) RenderEffects::
+return_new(RenderEffects *state) {
+  nassertr(state != (RenderEffects *)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(RenderEffects) 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: RenderEffects::determine_billboard
+//       Access: Private
+//  Description: This is the private implementation of
+//               get_billboard().
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+determine_billboard() {
+  const RenderEffect *effect = get_effect(BillboardEffect::get_class_type());
+  _billboard = (const BillboardEffect *)NULL;
+  if (effect != (const RenderEffect *)NULL) {
+    _billboard = DCAST(BillboardEffect, effect);
+  }
+  _flags |= F_checked_billboard;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::determine_decal
+//       Access: Private
+//  Description: This is the private implementation of has_decal().
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+determine_decal() {
+  const RenderEffect *effect = get_effect(DecalEffect::get_class_type());
+  if (effect != (const RenderEffect *)NULL) {
+    _flags |= F_has_decal;
+  }
+  _flags |= F_checked_decal;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               RenderEffects.
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+
+  int num_effects = _effects.size();
+  nassertv(num_effects == (int)(PN_uint16)num_effects);
+  dg.add_uint16(num_effects);
+
+  Effects::const_iterator ai;
+  for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
+    const Effect &effect = (*ai);
+
+    manager->write_pointer(dg, effect._effect);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int RenderEffects::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = TypedWritable::complete_pointers(p_list, manager);
+
+  // Get the effect pointers.
+  Effects::iterator ai;
+  for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
+    Effect &effect = (*ai);
+
+    effect._effect = DCAST(RenderEffect, p_list[pi++]);
+    nassertr(effect._effect != (RenderEffect *)NULL, pi);
+    effect._type = effect._effect->get_type();
+  }
+
+  // Now make sure the array is properly sorted.  (It won't
+  // necessarily preserve its correct sort after being read from bam,
+  // because the sort is based on TypeHandle indices, which can change
+  // from session to session.)
+  _effects.sort();
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::change_this
+//       Access: Public, Static
+//  Description: Called immediately after complete_pointers(), this
+//               gives the object a chance to adjust its own pointer
+//               if desired.  Most objects don't change pointers after
+//               completion, but some need to.
+//
+//               Once this function has been called, the old pointer
+//               will no longer be accessed.
+////////////////////////////////////////////////////////////////////
+TypedWritable *RenderEffects::
+change_this(TypedWritable *old_ptr, BamReader *manager) {
+  // First, uniquify the pointer.
+  RenderEffects *state = DCAST(RenderEffects, old_ptr);
+  CPT(RenderEffects) 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 (RenderEffects *)pointer.p();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::finalize
+//       Access: Public, Virtual
+//  Description: Called by the BamReader to perform any final actions
+//               needed for setting up the object after all objects
+//               have been read and all pointers have been completed.
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+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: RenderEffects::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type RenderEffects is encountered
+//               in the Bam file.  It should create the RenderEffects
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *RenderEffects::
+make_from_bam(const FactoryParams &params) {
+  RenderEffects *state = new RenderEffects;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  state->fillin(scan, manager);
+  manager->register_change_this(change_this, state);
+
+  return state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::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 RenderEffects.
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+
+  int num_effects = scan.get_uint16();
+
+  // Push back a NULL pointer for each effect for now, until we get
+  // the actual list of pointers later in complete_pointers().
+  _effects.reserve(num_effects);
+  for (int i = 0; i < num_effects; i++) {
+    manager->read_pointer(scan);
+    _effects.push_back(Effect());
+  }
+}

+ 174 - 0
panda/src/pgraph/renderEffects.h

@@ -0,0 +1,174 @@
+// Filename: renderEffects.h
+// Created by:  drose (14Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef RENDEREFFECTS_H
+#define RENDEREFFECTS_H
+
+#include "pandabase.h"
+
+#include "renderEffect.h"
+#include "typedWritableReferenceCount.h"
+#include "pointerTo.h"
+#include "indirectLess.h"
+#include "ordered_vector.h"
+
+class BillboardEffect;
+
+////////////////////////////////////////////////////////////////////
+//       Class : RenderEffects
+// Description : This represents a unique collection of RenderEffect
+//               objects that correspond to a particular renderable
+//               state.
+//
+//               You should not attempt to create or modify a
+//               RenderEffects object directly.  Instead, call one of
+//               the make() functions to create one for you.  And
+//               instead of modifying a RenderEffects object, create a
+//               new one.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA RenderEffects : public TypedWritableReferenceCount {
+protected:
+  RenderEffects();
+
+private:
+  RenderEffects(const RenderEffects &copy);
+  void operator = (const RenderEffects &copy);
+
+public:
+  virtual ~RenderEffects();
+
+  bool operator < (const RenderEffects &other) const;
+
+  bool safe_to_transform() const;
+  bool safe_to_combine() const;
+  CPT(RenderEffects) xform(const LMatrix4f &mat) const;
+
+PUBLISHED:
+  INLINE bool is_empty() const;
+  INLINE int get_num_effects() const;
+  INLINE const RenderEffect *get_effect(int n) const;
+
+  int find_effect(TypeHandle type) const;
+
+  static CPT(RenderEffects) make_empty();
+  static CPT(RenderEffects) make(const RenderEffect *effect);
+  static CPT(RenderEffects) make(const RenderEffect *effect1,
+                                 const RenderEffect *effect2);
+  static CPT(RenderEffects) make(const RenderEffect *effect1,
+                                 const RenderEffect *effect2,
+                                 const RenderEffect *effect3);
+  static CPT(RenderEffects) make(const RenderEffect *effect1,
+                                 const RenderEffect *effect2,
+                                 const RenderEffect *effect3,
+                                 const RenderEffect *effect4);
+
+  CPT(RenderEffects) add_effect(const RenderEffect *effect) const;
+  CPT(RenderEffects) remove_effect(TypeHandle type) const;
+
+  const RenderEffect *get_effect(TypeHandle type) const;
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level) const;
+
+public:
+  INLINE const BillboardEffect *get_billboard() const;
+  INLINE bool has_decal() const;
+
+private:
+  static CPT(RenderEffects) return_new(RenderEffects *state);
+  void determine_billboard();
+  void determine_decal();
+
+private:
+  typedef pset<const RenderEffects *, IndirectLess<RenderEffects> > States;
+  static States _states;
+  static CPT(RenderEffects) _empty_state;
+
+  // This iterator records the entry corresponding to this RenderEffects
+  // object in the above global set.  We keep the iterator around so
+  // we can remove it when the RenderEffects destructs.
+  States::iterator _saved_entry;
+
+private:
+  // This is the actual data within the RenderEffects: a set of
+  // RenderEffects.
+  class Effect {
+  public:
+    INLINE Effect(const RenderEffect *effect);
+    INLINE Effect();
+    INLINE Effect(TypeHandle type);
+    INLINE Effect(const Effect &copy);
+    INLINE void operator = (const Effect &copy);
+    INLINE bool operator < (const Effect &other) const;
+    INLINE int compare_to(const Effect &other) const;
+
+    TypeHandle _type;
+    CPT(RenderEffect) _effect;
+  };
+  typedef ov_set<Effect> Effects;
+  Effects _effects;
+
+  // We cache the pointer to the some critical effects stored in
+  // the state, if they exist.
+  const BillboardEffect *_billboard;
+
+  enum Flags {
+    F_checked_billboard    = 0x0001,
+    F_checked_decal        = 0x0002,
+    F_has_decal            = 0x0004,
+  };
+  short _flags;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+  static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
+  virtual void finalize();
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "RenderEffects",
+                  TypedWritableReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+INLINE ostream &operator << (ostream &out, const RenderEffects &state) {
+  state.output(out);
+  return out;
+}
+
+#include "renderEffects.I"
+
+#endif
+

+ 3 - 43
panda/src/pgraph/renderState.I

@@ -162,54 +162,14 @@ get_override(int n) const {
   return _attributes[n]._override;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: RenderState::get_billboard
-//       Access: Public
-//  Description: This function is provided as an optimization, to
-//               speed up the render-time checking for the existance
-//               of a BillboardAttrib on this state.  It returns a
-//               pointer to the BillboardAttrib, if there is one, or
-//               NULL if there is not.
-////////////////////////////////////////////////////////////////////
-INLINE const BillboardAttrib *RenderState::
-get_billboard() const {
-  if ((_flags & F_checked_billboard) == 0) {
-    // We pretend this function is const, even though it transparently
-    // modifies the internal billboard cache.
-    ((RenderState *)this)->determine_billboard();
-  }
-  return _billboard;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: RenderState::has_decal
-//       Access: Public
-//  Description: This function is provided as an optimization, to
-//               speed up the render-time checking for the existance
-//               of a DecalAttrib on this state.  It returns true if a
-//               DecalAttrib exists, false otherwise.  Note that since
-//               there is no additional information stored on the
-//               DecalAttrib, there's no point in returning it if it
-//               exists.
-////////////////////////////////////////////////////////////////////
-INLINE bool RenderState::
-has_decal() const {
-  if ((_flags & F_checked_decal) == 0) {
-    // We pretend this function is const, even though it transparently
-    // modifies the internal decal cache.
-    ((RenderState *)this)->determine_decal();
-  }
-  return ((_flags & F_has_decal) != 0);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::get_bin_index
 //       Access: Public
 //  Description: Returns the bin index indicated by the CullBinAttrib,
 //               if any, associated by this state (or the default bin
-//               index if there is no CullBinAttrib).  As in
-//               get_billboard(), above, this function is provided as
-//               an optimization.
+//               index if there is no CullBinAttrib).  This function
+//               is provided as an optimization for determining this
+//               at render time.
 ////////////////////////////////////////////////////////////////////
 INLINE int RenderState::
 get_bin_index() const {

+ 0 - 31
panda/src/pgraph/renderState.cxx

@@ -17,7 +17,6 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "renderState.h"
-#include "billboardAttrib.h"
 #include "transparencyAttrib.h"
 #include "cullBinAttrib.h"
 #include "cullBinManager.h"
@@ -868,36 +867,6 @@ do_invert_compose(const RenderState *other) const {
   return return_new(new_state);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: RenderState::determine_billboard
-//       Access: Private
-//  Description: This is the private implementation of
-//               get_billboard().
-////////////////////////////////////////////////////////////////////
-void RenderState::
-determine_billboard() {
-  const RenderAttrib *attrib = get_attrib(BillboardAttrib::get_class_type());
-  _billboard = (const BillboardAttrib *)NULL;
-  if (attrib != (const RenderAttrib *)NULL) {
-    _billboard = DCAST(BillboardAttrib, attrib);
-  }
-  _flags |= F_checked_billboard;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: RenderState::determine_decal
-//       Access: Private
-//  Description: This is the private implementation of has_decal().
-////////////////////////////////////////////////////////////////////
-void RenderState::
-determine_decal() {
-  const RenderAttrib *attrib = get_attrib(DecalAttrib::get_class_type());
-  if (attrib != (const RenderAttrib *)NULL) {
-    _flags |= F_has_decal;
-  }
-  _flags |= F_checked_decal;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::determine_bin_index
 //       Access: Private

+ 3 - 14
panda/src/pgraph/renderState.h

@@ -28,7 +28,6 @@
 #include "ordered_vector.h"
 
 class GraphicsStateGuardianBase;
-class BillboardAttrib;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : RenderState
@@ -89,8 +88,6 @@ PUBLISHED:
   void write(ostream &out, int indent_level) const;
 
 public:
-  INLINE const BillboardAttrib *get_billboard() const;
-  INLINE bool has_decal() const;
   INLINE int get_bin_index() const;
   INLINE int get_draw_order() const;
 
@@ -105,8 +102,6 @@ private:
   static CPT(RenderState) return_new(RenderState *state);
   CPT(RenderState) do_compose(const RenderState *other) const;
   CPT(RenderState) do_invert_compose(const RenderState *other) const;
-  void determine_billboard();
-  void determine_decal();
   void determine_bin_index();
 
 private:
@@ -161,19 +156,13 @@ private:
   typedef ov_set<Attribute> Attributes;
   Attributes _attributes;
 
-  // We cache the pointer to the some critical attributes stored in
-  // the state, if they exist.
-  const BillboardAttrib *_billboard;
-  
-  // We also cache the index to the associated GeomBin.
+  // We cache the index to the associated CullBin, if there happens to
+  // be a CullBinAttrib in the state.
   int _bin_index;
   int _draw_order;
 
   enum Flags {
-    F_checked_billboard    = 0x0001,
-    F_checked_bin_index    = 0x0002,
-    F_checked_decal        = 0x0004,
-    F_has_decal            = 0x0008,
+    F_checked_bin_index    = 0x0001,
   };
   short _flags;