Forráskód Böngészése

add NodePath::set_light() interfaces

David Rose 21 éve
szülő
commit
a60eb87e19

+ 3 - 6
panda/src/display/graphicsStateGuardian.cxx

@@ -676,12 +676,9 @@ issue_light(const LightAttrib *attrib) {
   bool any_bound = false;
 
   int num_enabled = 0;
-  int num_lights = attrib->get_num_lights();
-  if (attrib->get_operation() == LightAttrib::O_remove) {
-    num_lights = 0;
-  }
-  for (int li = 0; li < num_lights; li++) {
-    Light *light = attrib->get_light(li);
+  int num_on_lights = attrib->get_num_on_lights();
+  for (int li = 0; li < num_on_lights; li++) {
+    Light *light = attrib->get_on_light(li);
     nassertv(light != (Light *)NULL);
 
     num_enabled++;

+ 71 - 56
panda/src/pgraph/lightAttrib.I

@@ -25,95 +25,110 @@
 ////////////////////////////////////////////////////////////////////
 INLINE LightAttrib::
 LightAttrib() {
-  _operation = O_set;
+  _off_all_lights = false;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::get_operation
-//       Access: Published
-//  Description: Returns the basic operation type of the LightAttrib.
-//               If this is O_set, the lights listed here completely
-//               replace any lights that were already on.  If this is
-//               O_add, the lights here are added to the set of of
-//               lights that were already on, and if O_remove, the
-//               lights here are removed from the set of lights that
-//               were on.
-////////////////////////////////////////////////////////////////////
-INLINE LightAttrib::Operation LightAttrib::
-get_operation() const {
-  return _operation;
+//     Function: LightAttrib::Copy Constructor
+//       Access: Private
+//  Description: Use LightAttrib::make() to construct a new
+//               LightAttrib object.  The copy constructor is only
+//               defined to facilitate methods like add_on_light().
+////////////////////////////////////////////////////////////////////
+INLINE LightAttrib::
+LightAttrib(const LightAttrib &copy) :
+  _on_lights(copy._on_lights),
+  _off_lights(copy._off_lights),
+  _off_all_lights(copy._off_all_lights)
+{
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::get_num_lights
+//     Function: LightAttrib::get_num_on_lights
 //       Access: Published
-//  Description: Returns the number of lights listed in the attribute.
+//  Description: Returns the number of lights that are turned on by
+//               the attribute.
 ////////////////////////////////////////////////////////////////////
 INLINE int LightAttrib::
-get_num_lights() const {
-  return _lights.size();
+get_num_on_lights() const {
+  return _on_lights.size();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::get_light
+//     Function: LightAttrib::get_on_light
 //       Access: Published
-//  Description: Returns the nth lights listed in the attribute.
+//  Description: Returns the nth light turned on by the attribute,
+//               sorted in render order.
 ////////////////////////////////////////////////////////////////////
 INLINE Light *LightAttrib::
-get_light(int n) const {
-  nassertr(n >= 0 && n < (int)_lights.size(), (Light *)NULL);
-  return _lights[n];
+get_on_light(int n) const {
+  nassertr(n >= 0 && n < (int)_on_lights.size(), (Light *)NULL);
+  return _on_lights[n];
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::add_light
+//     Function: LightAttrib::has_on_light
 //       Access: Published
-//  Description: Returns a new LightAttrib, just like this one, but
-//               with the indicated light added to the list of lights.
-////////////////////////////////////////////////////////////////////
-INLINE CPT(RenderAttrib) LightAttrib::
-add_light(Light *light) const {
-  if (_operation == O_remove) {  
-    return compose(make(O_remove, light));
-  } else {
-    return compose(make(O_add, light));
-  }
+//  Description: Returns true if the indicated light is turned on by
+//               the attrib, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool LightAttrib::
+has_on_light(Light *light) const {
+  return _on_lights.find(light) != _on_lights.end();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::remove_light
+//     Function: LightAttrib::get_num_off_lights
 //       Access: Published
-//  Description: Returns a new LightAttrib, just like this one, but
-//               with the indicated light removed from the list of
-//               lights.
-////////////////////////////////////////////////////////////////////
-INLINE CPT(RenderAttrib) LightAttrib::
-remove_light(Light *light) const {
-  if (_operation == O_remove) {  
-    return compose(make(O_add, light));
-  } else {
-    return compose(make(O_remove, light));
-  }
+//  Description: Returns the number of lights that are turned off by
+//               the attribute.
+////////////////////////////////////////////////////////////////////
+INLINE int LightAttrib::
+get_num_off_lights() const {
+  return _off_lights.size();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::is_identity
+//     Function: LightAttrib::get_off_light
 //       Access: Published
-//  Description: Returns true if this is an identity attrib: it does
-//               not change the set of lights in use.
+//  Description: Returns the nth light turned off by the attribute,
+//               sorted in arbitrary (pointer) order.
+////////////////////////////////////////////////////////////////////
+INLINE Light *LightAttrib::
+get_off_light(int n) const {
+  nassertr(n >= 0 && n < (int)_off_lights.size(), (Light *)NULL);
+  return _off_lights[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::has_off_light
+//       Access: Published
+//  Description: Returns true if the indicated light is turned off by
+//               the attrib, false otherwise.
 ////////////////////////////////////////////////////////////////////
 INLINE bool LightAttrib::
-is_identity() const {
-  return _operation != O_set && _lights.empty();
+has_off_light(Light *light) const {
+  return _off_all_lights || _off_lights.find(light) != _off_lights.end();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::has_all_off
+//       Access: Published
+//  Description: Returns true if this attrib turns off all lights
+//               (although it may also turn some on).
+////////////////////////////////////////////////////////////////////
+INLINE bool LightAttrib::
+has_all_off() const {
+  return _off_all_lights;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::is_all_off
+//     Function: LightAttrib::is_identity
 //       Access: Published
-//  Description: Returns true if this attrib turns off all lights and
-//               turns none on.
+//  Description: Returns true if this is an identity attrib: it does
+//               not change the set of lights in use.
 ////////////////////////////////////////////////////////////////////
 INLINE bool LightAttrib::
-is_all_off() const {
-  return _operation == O_set && _lights.empty();
+is_identity() const {
+  return _on_lights.empty() && _off_lights.empty() && !_off_all_lights;
 }

+ 544 - 188
panda/src/pgraph/lightAttrib.cxx

@@ -24,33 +24,42 @@
 #include "datagram.h"
 #include "datagramIterator.h"
 
+CPT(RenderAttrib) LightAttrib::_empty_attrib;
+CPT(RenderAttrib) LightAttrib::_all_off_attrib;
 TypeHandle LightAttrib::_type_handle;
 
-////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::make_all_off
-//       Access: Published, Static
-//  Description: Constructs a new LightAttrib object that turns off
-//               all lights (and hence disables lighting).
-////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) LightAttrib::
-make_all_off() {
-  LightAttrib *attrib = new LightAttrib;
-  attrib->_operation = O_set;
-  return return_new(attrib);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: LightAttrib::make
 //       Access: Published, Static
 //  Description: Constructs a new LightAttrib object that turns on (or
-//               off, according to op) the indicate light(s).
+//               off, according to op) the indicated light(s).
+//
+//               This method is now deprecated.  Use add_on_light() or
+//               add_off_light() instead.
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) LightAttrib::
 make(LightAttrib::Operation op, Light *light) {
-  LightAttrib *attrib = new LightAttrib;
-  attrib->_operation = op;
-  attrib->_lights.push_back(light);
-  return return_new(attrib);
+  CPT(RenderAttrib) attrib;
+
+  switch (op) {
+  case O_set:
+    attrib = make_all_off();
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light);
+    return attrib;
+   
+  case O_add:
+    attrib = make();
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light);
+    return attrib;
+
+  case O_remove:
+    attrib = make();
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light);
+    return attrib;
+  }
+
+  nassertr(false, make());
+  return make();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -58,16 +67,36 @@ make(LightAttrib::Operation op, Light *light) {
 //       Access: Published, Static
 //  Description: Constructs a new LightAttrib object that turns on (or
 //               off, according to op) the indicate light(s).
+//
+//               This method is now deprecated.  Use add_on_light() or
+//               add_off_light() instead.
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) LightAttrib::
 make(LightAttrib::Operation op, Light *light1, Light *light2) {
-  LightAttrib *attrib = new LightAttrib;
-  attrib->_operation = op;
-  attrib->_lights.push_back(light1);
-  attrib->_lights.push_back(light2);
+  CPT(RenderAttrib) attrib;
+
+  switch (op) {
+  case O_set:
+    attrib = make_all_off();
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light1);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light2);
+    return attrib;
+   
+  case O_add:
+    attrib = make();
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light1);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light2);
+    return attrib;
+
+  case O_remove:
+    attrib = make();
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light1);
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light2);
+    return attrib;
+  }
 
-  attrib->_lights.sort();
-  return return_new(attrib);
+  nassertr(false, make());
+  return make();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -75,18 +104,40 @@ make(LightAttrib::Operation op, Light *light1, Light *light2) {
 //       Access: Published, Static
 //  Description: Constructs a new LightAttrib object that turns on (or
 //               off, according to op) the indicate light(s).
+//
+//               This method is now deprecated.  Use add_on_light() or
+//               add_off_light() instead.
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) LightAttrib::
 make(LightAttrib::Operation op, Light *light1, Light *light2,
      Light *light3) {
-  LightAttrib *attrib = new LightAttrib;
-  attrib->_operation = op;
-  attrib->_lights.push_back(light1);
-  attrib->_lights.push_back(light2);
-  attrib->_lights.push_back(light3);
+  CPT(RenderAttrib) attrib;
+
+  switch (op) {
+  case O_set:
+    attrib = make_all_off();
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light1);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light2);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light3);
+    return attrib;
+   
+  case O_add:
+    attrib = make();
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light1);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light2);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light3);
+    return attrib;
+
+  case O_remove:
+    attrib = make();
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light1);
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light2);
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light3);
+    return attrib;
+  }
 
-  attrib->_lights.sort();
-  return return_new(attrib);
+  nassertr(false, make());
+  return make();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -94,19 +145,110 @@ make(LightAttrib::Operation op, Light *light1, Light *light2,
 //       Access: Published, Static
 //  Description: Constructs a new LightAttrib object that turns on (or
 //               off, according to op) the indicate light(s).
+//
+//               This method is now deprecated.  Use add_on_light() or
+//               add_off_light() instead.
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) LightAttrib::
 make(LightAttrib::Operation op, Light *light1, Light *light2,
      Light *light3, Light *light4) {
-  LightAttrib *attrib = new LightAttrib;
-  attrib->_operation = op;
-  attrib->_lights.push_back(light1);
-  attrib->_lights.push_back(light2);
-  attrib->_lights.push_back(light3);
-  attrib->_lights.push_back(light4);
+  CPT(RenderAttrib) attrib;
+
+  switch (op) {
+  case O_set:
+    attrib = make_all_off();
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light1);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light2);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light3);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light4);
+    return attrib;
+   
+  case O_add:
+    attrib = make();
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light1);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light2);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light3);
+    attrib = DCAST(LightAttrib, attrib)->add_on_light(light4);
+    return attrib;
+
+  case O_remove:
+    attrib = make();
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light1);
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light2);
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light3);
+    attrib = DCAST(LightAttrib, attrib)->add_off_light(light4);
+    return attrib;
+  }
 
-  attrib->_lights.sort();
-  return return_new(attrib);
+  nassertr(false, make());
+  return make();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::get_operation
+//       Access: Published
+//  Description: Returns the basic operation type of the LightAttrib.
+//               If this is O_set, the lights listed here completely
+//               replace any lights that were already on.  If this is
+//               O_add, the lights here are added to the set of of
+//               lights that were already on, and if O_remove, the
+//               lights here are removed from the set of lights that
+//               were on.
+//
+//               This method is now deprecated.  LightAttribs nowadays
+//               have a separate list of on_lights and off_lights, so
+//               this method doesn't make sense.  Query the lists
+//               independently.
+////////////////////////////////////////////////////////////////////
+LightAttrib::Operation LightAttrib::
+get_operation() const {
+  if (has_all_off()) {
+    return O_set;
+
+  } else if (get_num_on_lights() != 0) {
+    return O_add;
+
+  } else {
+    return O_remove;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::get_num_lights
+//       Access: Published
+//  Description: Returns the number of lights listed in the attribute.
+//
+//               This method is now deprecated.  LightAttribs nowadays
+//               have a separate list of on_lights and off_lights, so
+//               this method doesn't make sense.  Query the lists
+//               independently.
+////////////////////////////////////////////////////////////////////
+int LightAttrib::
+get_num_lights() const {
+  if (get_num_on_lights() != 0) {
+    return get_num_on_lights();
+  } else {
+    return get_num_off_lights();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::get_light
+//       Access: Published
+//  Description: Returns the nth lights listed in the attribute.
+//
+//               This method is now deprecated.  LightAttribs nowadays
+//               have a separate list of on_lights and off_lights, so
+//               this method doesn't make sense.  Query the lists
+//               independently.
+////////////////////////////////////////////////////////////////////
+Light *LightAttrib::
+get_light(int n) const {
+  if (get_num_on_lights() != 0) {
+    return get_on_light(n);
+  } else {
+    return get_off_light(n);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -114,10 +256,160 @@ make(LightAttrib::Operation op, Light *light1, Light *light2,
 //       Access: Published
 //  Description: Returns true if the indicated light is listed in the
 //               attrib, false otherwise.
+//
+//               This method is now deprecated.  LightAttribs nowadays
+//               have a separate list of on_lights and off_lights, so
+//               this method doesn't make sense.  Query the lists
+//               independently.
 ////////////////////////////////////////////////////////////////////
 bool LightAttrib::
 has_light(Light *light) const {
-  return _lights.find(light) != _lights.end();
+  if (get_num_on_lights() != 0) {
+    return has_on_light(light);
+  } else {
+    return has_off_light(light);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::add_light
+//       Access: Published
+//  Description: Returns a new LightAttrib, just like this one, but
+//               with the indicated light added to the list of lights.
+//
+//               This method is now deprecated.  Use add_on_light() or
+//               add_off_light() instead.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) LightAttrib::
+add_light(Light *light) const {
+  if (get_num_on_lights() != 0) {
+    return add_on_light(light);
+  } else {
+    return add_off_light(light);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::remove_light
+//       Access: Published
+//  Description: Returns a new LightAttrib, just like this one, but
+//               with the indicated light removed from the list of
+//               lights.
+//
+//               This method is now deprecated.  Use remove_on_light()
+//               or remove_off_light() instead.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) LightAttrib::
+remove_light(Light *light) const {
+  if (get_num_on_lights() != 0) {
+    return remove_on_light(light);
+  } else {
+    return remove_off_light(light);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new LightAttrib object that does
+//               nothing.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) LightAttrib::
+make() {
+  // We make it a special case and store a pointer to the empty attrib
+  // forever once we find it the first time, as an optimization.
+  if (_empty_attrib == (RenderAttrib *)NULL) {
+    _empty_attrib = return_new(new LightAttrib);
+  }
+
+  return _empty_attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::make_all_off
+//       Access: Published, Static
+//  Description: Constructs a new LightAttrib object that turns off
+//               all lights (and hence disables lighting).
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) LightAttrib::
+make_all_off() {
+  // We make it a special case and store a pointer to the off attrib
+  // forever once we find it the first time, as an optimization.
+  if (_all_off_attrib == (RenderAttrib *)NULL) {
+    LightAttrib *attrib = new LightAttrib;
+    attrib->_off_all_lights = true;
+    _all_off_attrib = return_new(attrib);
+  }
+
+  return _all_off_attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::add_on_light
+//       Access: Published
+//  Description: Returns a new LightAttrib, just like this one, but
+//               with the indicated light added to the list of lights
+//               turned on by this attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) LightAttrib::
+add_on_light(Light *light) const {
+  LightAttrib *attrib = new LightAttrib(*this);
+  attrib->_on_lights.insert(light);
+  attrib->_off_lights.erase(light);
+
+  pair<Lights::iterator, bool> insert_result = 
+    attrib->_on_lights.insert(Lights::value_type(light));
+  if (insert_result.second) {
+    // Also ensure it is removed from the off_lights list.
+    attrib->_off_lights.erase(light);
+  }
+
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::remove_on_light
+//       Access: Published
+//  Description: Returns a new LightAttrib, just like this one, but
+//               with the indicated light removed from the list of
+//               lights turned on by this attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) LightAttrib::
+remove_on_light(Light *light) const {
+  LightAttrib *attrib = new LightAttrib(*this);
+  attrib->_on_lights.erase(light);
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::add_off_light
+//       Access: Published
+//  Description: Returns a new LightAttrib, just like this one, but
+//               with the indicated light added to the list of lights
+//               turned off by this attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) LightAttrib::
+add_off_light(Light *light) const {
+  LightAttrib *attrib = new LightAttrib(*this);
+  if (!_off_all_lights) {
+    attrib->_off_lights.insert(light);
+  }
+  attrib->_on_lights.erase(light);
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::remove_off_light
+//       Access: Published
+//  Description: Returns a new LightAttrib, just like this one, but
+//               with the indicated light removed from the list of
+//               lights turned off by this attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) LightAttrib::
+remove_off_light(Light *light) const {
+  LightAttrib *attrib = new LightAttrib(*this);
+  attrib->_off_lights.erase(light);
+  return return_new(attrib);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -142,27 +434,39 @@ issue(GraphicsStateGuardianBase *gsg) const {
 void LightAttrib::
 output(ostream &out) const {
   out << get_type() << ":";
-  if (_operation == O_set && _lights.empty()) {
-    out << "all off";
+  if (_off_lights.empty()) {
+    if (_on_lights.empty()) {
+      if (_off_all_lights) {
+        out << "all off";
+      } else {
+        out << "identity";
+      }
+    } else {
+      if (_off_all_lights) {
+        out << "set";
+      } else {
+        out << "on";
+      }
+    }
+
   } else {
-    switch (_operation) {
-    case O_set:
-      out << "set";
-      break;
-    case O_add:
-      out << "add";
-      break;
-    case O_remove:
-      out << "remove";
-      break;
+    out << "off";
+    Lights::const_iterator fi;
+    for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
+      Light *light = (*fi);
+      out << " " << *light;
     }
 
-    Lights::const_iterator li;
-    for (li = _lights.begin(); li != _lights.end(); ++li) {
-      Light *light = (*li);
-      out << " " << light->get_type();
+    if (!_on_lights.empty()) {
+      out << " on";
     }
   }
+    
+  Lights::const_iterator li;
+  for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
+    Light *light = (*li);
+    out << " " << *light;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -185,14 +489,14 @@ compare_to_impl(const RenderAttrib *other) const {
   const LightAttrib *ta;
   DCAST_INTO_R(ta, other, 0);
 
-  if (_operation != ta->_operation) {
-    return (int)_operation - (int)ta->_operation;
+  if (_off_all_lights != ta->_off_all_lights) {
+    return (int)_off_all_lights - (int)ta->_off_all_lights;
   }
 
-  Lights::const_iterator li = _lights.begin();
-  Lights::const_iterator oli = ta->_lights.begin();
+  Lights::const_iterator li = _on_lights.begin();
+  Lights::const_iterator oli = ta->_on_lights.begin();
 
-  while (li != _lights.end() && oli != ta->_lights.end()) {
+  while (li != _on_lights.end() && oli != ta->_on_lights.end()) {
     Light *light = (*li);
     Light *other_light = (*oli);
 
@@ -204,10 +508,32 @@ compare_to_impl(const RenderAttrib *other) const {
     ++oli;
   }
 
-  if (li != _lights.end()) {
+  if (li != _on_lights.end()) {
     return 1;
   }
-  if (oli != ta->_lights.end()) {
+  if (oli != ta->_on_lights.end()) {
+    return -1;
+  }
+
+  Lights::const_iterator fi = _off_lights.begin();
+  Lights::const_iterator ofi = ta->_off_lights.begin();
+
+  while (fi != _off_lights.end() && ofi != ta->_off_lights.end()) {
+    Light *light = (*fi);
+    Light *other_light = (*ofi);
+
+    if (light != other_light) {
+      return light < other_light ? -1 : 1;
+    }
+
+    ++fi;
+    ++ofi;
+  }
+
+  if (fi != _off_lights.end()) {
+    return 1;
+  }
+  if (ofi != ta->_off_lights.end()) {
     return -1;
   }
   
@@ -236,95 +562,78 @@ compose_impl(const RenderAttrib *other) const {
   const LightAttrib *ta;
   DCAST_INTO_R(ta, other, 0);
 
-  if (ta->_operation == O_set) {
-    // If the other type is O_set, it doesn't matter what we are.
-    return ta;
-  }
-
-  if (_operation == ta->_operation) {
-    // If the operation types match, the composition is simply the
-    // union.
-    return do_add(ta, _operation);
-
-  } else if (ta->_operation == O_remove) {
-    // If the other operation type is remove, and our type is add or
-    // set, then remove.
-    return do_remove(ta, _operation);
-
-  } else if (_operation == O_remove) {
-    // If our type is remove, then the other one wins.
+  if (ta->_off_all_lights) {
+    // If the other type turns off all lights, it doesn't matter what
+    // we are.
     return ta;
-
-  } else {
-    // Otherwise, the result is the union.
-    return do_add(ta, _operation);
   }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::invert_compose_impl
-//       Access: Protected, Virtual
-//  Description: Intended to be overridden by derived RenderAttrib
-//               types to specify how two consecutive RenderAttrib
-//               objects of the same type interact.
-//
-//               See invert_compose() and compose_impl().
-////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) LightAttrib::
-invert_compose_impl(const RenderAttrib *other) const {
-  // I think in this case the other attrib always wins.  Maybe this
-  // needs a bit more thought.  It's hard to imagine that it's even
-  // important to compute this properly.
-  return other;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::make_default_impl
-//       Access: Protected, Virtual
-//  Description: Intended to be overridden by derived LightAttrib
-//               types to specify what the default property for a
-//               LightAttrib of this type should be.
-//
-//               This should return a newly-allocated LightAttrib of
-//               the same type that corresponds to whatever the
-//               standard default for this kind of LightAttrib is.
-////////////////////////////////////////////////////////////////////
-RenderAttrib *LightAttrib::
-make_default_impl() const {
-  return new LightAttrib;
-}
 
-////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::do_add
-//       Access: Private
-//  Description: Returns a new LightAttrib that represents all the
-//               lights of this attrib, with those of the other one
-//               added in.
-////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) LightAttrib::
-do_add(const LightAttrib *other, LightAttrib::Operation op) const {
-  Lights::const_iterator ai = _lights.begin();
-  Lights::const_iterator bi = other->_lights.begin();
+  // This is a three-way merge between ai, bi, and ci, except that bi
+  // and ci should have no intersection and therefore needn't be
+  // compared to each other.
+  Lights::const_iterator ai = _on_lights.begin();
+  Lights::const_iterator bi = ta->_on_lights.begin();
+  Lights::const_iterator ci = ta->_off_lights.begin();
 
   // Create a new LightAttrib that will hold the result.
   LightAttrib *new_attrib = new LightAttrib;
-  new_attrib->_operation = op;
   back_insert_iterator<Lights> result = 
-    back_inserter(new_attrib->_lights);
+    back_inserter(new_attrib->_on_lights);
 
-  while (ai != _lights.end() && bi != other->_lights.end()) {
+  while (ai != _on_lights.end() && 
+         bi != ta->_on_lights.end() && 
+         ci != ta->_off_lights.end()) {
+    if ((*ai) < (*bi)) {
+      if ((*ai) < (*ci)) {
+        // Here is a light that we have in the original, which is not
+        // present in the secondary.
+        *result = *ai;
+        ++ai;
+        ++result;
+
+      } else if ((*ci) < (*ai)) {
+        // Here is a light that is turned off in the secondary, but
+        // was not present in the original.
+        ++ci;
+
+      } else { // (*ci) == (*ai)
+        // Here is a light that is turned off in the secondary, and
+        // was present in the original.
+        ++ai;
+        ++ci;
+      }
+
+    } else if ((*bi) < (*ai)) {
+      // Here is a new light we have in the secondary, that was not
+      // present in the original.
+      *result = *bi;
+      ++bi;
+      ++result;
+
+    } else {  // (*bi) == (*ai)
+      // Here is a light we have in both.
+      *result = *ai;
+      ++ai;
+      ++bi;
+      ++result;
+    }
+  }
+
+  while (ai != _on_lights.end() && bi != ta->_on_lights.end()) {
     if ((*ai) < (*bi)) {
       // Here is a light that we have in the original, which is not
       // present in the secondary.
       *result = *ai;
       ++ai;
       ++result;
+
     } else if ((*bi) < (*ai)) {
       // Here is a new light we have in the secondary, that was not
       // present in the original.
       *result = *bi;
       ++bi;
       ++result;
+
     } else {
       // Here is a light we have in both.
       *result = *ai;
@@ -334,13 +643,34 @@ do_add(const LightAttrib *other, LightAttrib::Operation op) const {
     }
   }
 
-  while (ai != _lights.end()) {
+  while (ai != _on_lights.end() && ci != ta->_off_lights.end()) {
+    if ((*ai) < (*ci)) {
+      // Here is a light that we have in the original, which is not
+      // present in the secondary.
+      *result = *ai;
+      ++ai;
+      ++result;
+      
+    } else if ((*ci) < (*ai)) {
+      // Here is a light that is turned off in the secondary, but
+      // was not present in the original.
+      ++ci;
+      
+    } else { // (*ci) == (*ai)
+      // Here is a light that is turned off in the secondary, and
+      // was present in the original.
+      ++ai;
+      ++ci;
+    }
+  }
+
+  while (ai != _on_lights.end()) {
     *result = *ai;
     ++ai;
     ++result;
   }
 
-  while (bi != other->_lights.end()) {
+  while (bi != ta->_on_lights.end()) {
     *result = *bi;
     ++bi;
     ++result;
@@ -350,48 +680,36 @@ do_add(const LightAttrib *other, LightAttrib::Operation op) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LightAttrib::do_remove
-//       Access: Private
-//  Description: Returns a new LightAttrib that represents all the
-//               lights of this attrib, with those of the other one
-//               removed.
+//     Function: LightAttrib::invert_compose_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to specify how two consecutive RenderAttrib
+//               objects of the same type interact.
+//
+//               See invert_compose() and compose_impl().
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) LightAttrib::
-do_remove(const LightAttrib *other, LightAttrib::Operation op) const {
-  Lights::const_iterator ai = _lights.begin();
-  Lights::const_iterator bi = other->_lights.begin();
-
-  // Create a new LightAttrib that will hold the result.
-  LightAttrib *new_attrib = new LightAttrib;
-  new_attrib->_operation = op;
-  back_insert_iterator<Lights> result = 
-    back_inserter(new_attrib->_lights);
-
-  while (ai != _lights.end() && bi != other->_lights.end()) {
-    if ((*ai) < (*bi)) {
-      // Here is a light that we have in the original, which is
-      // not present in the secondary.  Keep it.
-      *result = *ai;
-      ++ai;
-      ++result;
-    } else if ((*bi) < (*ai)) {
-      // Here is a new light we have in the secondary, that was
-      // not present in the original.  Ignore it.
-      ++bi;
-    } else {
-      // Here is a light we have in both.  Drop it.
-      ++ai;
-      ++bi;
-    }
-  }
-
-  while (ai != _lights.end()) {
-    *result = *ai;
-    ++ai;
-    ++result;
-  }
+invert_compose_impl(const RenderAttrib *other) const {
+  // I think in this case the other attrib always wins.  Maybe this
+  // needs a bit more thought.  It's hard to imagine that it's even
+  // important to compute this properly.
+  return other;
+}
 
-  return return_new(new_attrib);
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::make_default_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived LightAttrib
+//               types to specify what the default property for a
+//               LightAttrib of this type should be.
+//
+//               This should return a newly-allocated LightAttrib of
+//               the same type that corresponds to whatever the
+//               standard default for this kind of LightAttrib is.
+////////////////////////////////////////////////////////////////////
+RenderAttrib *LightAttrib::
+make_default_impl() const {
+  return new LightAttrib;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -415,14 +733,23 @@ void LightAttrib::
 write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
 
-  dg.add_int8((int)_operation);
-  PN_uint16 num_lights = _lights.size();
-  nassertv(num_lights == _lights.size());
-  dg.add_uint16(num_lights);
+  dg.add_bool(_off_all_lights);
 
-  Lights::const_iterator li;
-  for (li = _lights.begin(); li != _lights.end(); ++li) {
-    Light *light = (*li);
+  // write the number of off_lights
+  dg.add_uint16(get_num_off_lights());
+  // write the off lights pointers if any
+  Lights::const_iterator fi;
+  for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
+    Light *light = (*fi);
+    manager->write_pointer(dg, light->as_node());
+  }
+
+  // write the number of on lights
+  dg.add_uint16(get_num_on_lights());
+  // write the on lights pointers if any
+  Lights::const_iterator nti;
+  for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
+    Light *light = (*nti);
     manager->write_pointer(dg, light->as_node());
   }
 }
@@ -438,11 +765,20 @@ int LightAttrib::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = RenderAttrib::complete_pointers(p_list, manager);
 
-  Lights::iterator li;
-  for (li = _lights.begin(); li != _lights.end(); ++li) {
+  Lights::iterator ci = _off_lights.begin();
+  while (ci != _off_lights.end()) {
+    PandaNode *node;
+    DCAST_INTO_R(node, p_list[pi++], pi);
+    (*ci) = node->as_light();
+    ++ci;
+  }
+
+  ci = _on_lights.begin();
+  while (ci != _on_lights.end()) {
     PandaNode *node;
     DCAST_INTO_R(node, p_list[pi++], pi);
-    (*li) = node->as_light();
+    (*ci) = node->as_light();
+    ++ci;
   }
 
   return pi;
@@ -479,11 +815,31 @@ void LightAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
 
-  _operation = (Operation)scan.get_int8();
-  int num_lights = scan.get_uint16();
-
-  for (int i = 0; i < num_lights; i++) {
+  // We cheat a little bit here.  In the middle of bam version 4.10,
+  // we completely redefined the bam storage definition for
+  // LightAttribs, without bothering to up the bam version or even to
+  // attempt to read the old definition.  We get away with this,
+  // knowing that the egg loader doesn't create LightAttribs, and
+  // hence no old bam files have the old definition for LightAttrib
+  // within them.
+
+  _off_all_lights = scan.get_bool();
+
+  int num_off_lights = scan.get_uint16();
+    
+  // Push back a NULL pointer for each off Light for now, until
+  // we get the actual list of pointers later in complete_pointers().
+  _off_lights.reserve(num_off_lights);
+  int i;
+  for (i = 0; i < num_off_lights; i++) {
+    manager->read_pointer(scan);
+    _off_lights.push_back(NULL);
+  }
+    
+  int num_on_lights = scan.get_uint16();
+  _on_lights.reserve(num_on_lights);
+  for (i = 0; i < num_on_lights; i++) {
     manager->read_pointer(scan);
-    _lights.push_back(NULL);
+    _on_lights.push_back(NULL);
   }
 }

+ 35 - 14
panda/src/pgraph/lightAttrib.h

@@ -35,15 +35,18 @@
 class EXPCL_PANDA LightAttrib : public RenderAttrib {
 private:
   INLINE LightAttrib();
+  INLINE LightAttrib(const LightAttrib &copy);
 
 PUBLISHED:
+
+  // This is the old, deprecated interface to LightAttrib.  Do not use
+  // any of these methods for new code; these methods will be removed
+  // soon.
   enum Operation {
     O_set,
     O_add,
     O_remove
   };
-
-  static CPT(RenderAttrib) make_all_off();
   static CPT(RenderAttrib) make(Operation op, 
                                 Light *light);
   static CPT(RenderAttrib) make(Operation op, 
@@ -55,17 +58,36 @@ PUBLISHED:
                                 Light *light1, Light *light2,
                                 Light *light3, Light *light4);
 
-  INLINE Operation get_operation() const;
+  Operation get_operation() const;
 
-  INLINE int get_num_lights() const;
-  INLINE Light *get_light(int n) const;
+  int get_num_lights() const;
+  Light *get_light(int n) const;
   bool has_light(Light *light) const;
 
-  INLINE CPT(RenderAttrib) add_light(Light *light) const;
-  INLINE CPT(RenderAttrib) remove_light(Light *light) const;
+  CPT(RenderAttrib) add_light(Light *light) const;
+  CPT(RenderAttrib) remove_light(Light *light) const;
+
+
+  // The following is the new, more general interface to the
+  // LightAttrib.
+  static CPT(RenderAttrib) make();
+  static CPT(RenderAttrib) make_all_off();
+
+  INLINE int get_num_on_lights() const;
+  INLINE Light *get_on_light(int n) const;
+  INLINE bool has_on_light(Light *light) const;
+
+  INLINE int get_num_off_lights() const;
+  INLINE Light *get_off_light(int n) const;
+  INLINE bool has_off_light(Light *light) const;
+  INLINE bool has_all_off() const;
 
   INLINE bool is_identity() const;
-  INLINE bool is_all_off() const;
+
+  CPT(RenderAttrib) add_on_light(Light *light) const;
+  CPT(RenderAttrib) remove_on_light(Light *light) const;
+  CPT(RenderAttrib) add_off_light(Light *light) const;
+  CPT(RenderAttrib) remove_off_light(Light *light) const;
 
 public:
   virtual void issue(GraphicsStateGuardianBase *gsg) const;
@@ -78,13 +100,12 @@ protected:
   virtual RenderAttrib *make_default_impl() const;
 
 private:
-  CPT(RenderAttrib) do_add(const LightAttrib *other, Operation op) const;
-  CPT(RenderAttrib) do_remove(const LightAttrib *other, Operation op) const;
-
-private:
-  Operation _operation;
   typedef ov_set< PT(Light) > Lights;
-  Lights _lights;
+  Lights _on_lights, _off_lights;
+  bool _off_all_lights;
+
+  static CPT(RenderAttrib) _empty_attrib;
+  static CPT(RenderAttrib) _all_off_attrib;
 
 public:
   static void register_with_read_factory();

+ 291 - 0
panda/src/pgraph/nodePath.cxx

@@ -26,6 +26,8 @@
 #include "cullBinAttrib.h"
 #include "textureAttrib.h"
 #include "materialAttrib.h"
+#include "lightAttrib.h"
+#include "polylightEffect.h"
 #include "fogAttrib.h"
 #include "renderModeAttrib.h"
 #include "cullFaceAttrib.h"
@@ -2014,6 +2016,295 @@ get_color_scale() const {
   return ident_scale;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_light
+//       Access: Published
+//  Description: Adds the indicated light to the list of lights that
+//               illuminate geometry at this node and below.  The
+//               light itself should be parented into the scene graph
+//               elsewhere, to represent the light's position in
+//               space; but until set_light() is called it will
+//               illuminate no geometry.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_light(Light *light, int priority) {
+  nassertv_always(!is_empty());
+
+  const RenderAttrib *attrib =
+    node()->get_attrib(LightAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    priority = max(priority,
+                   node()->get_state()->get_override(LightAttrib::get_class_type()));
+    const LightAttrib *la = DCAST(LightAttrib, attrib);
+
+    // Modify the existing LightAttrib to add the indicated
+    // light.
+    node()->set_attrib(la->add_on_light(light), priority);
+
+  } else {
+    // Create a new LightAttrib for this node.
+    CPT(LightAttrib) la = DCAST(LightAttrib, LightAttrib::make());
+    node()->set_attrib(la->add_on_light(light), priority);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_light_off
+//       Access: Published
+//  Description: Sets the geometry at this level and below to render
+//               using no lights at all.  This is different
+//               from not specifying a light; rather, this
+//               specifically contradicts set_light() at a higher
+//               node level (or, with a priority, overrides a
+//               set_light() at a lower level).
+//
+//               If no lights are in effect on a particular piece of
+//               geometry, that geometry is rendered with lighting
+//               disabled.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_light_off(int priority) {
+  nassertv_always(!is_empty());
+  node()->set_attrib(LightAttrib::make_all_off(), priority);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_light_off
+//       Access: Published
+//  Description: Sets the geometry at this level and below to render
+//               using without the indicated light.  This is different
+//               from not specifying the light; rather, this
+//               specifically contradicts set_light() at a higher node
+//               level (or, with a priority, overrides a set_light()
+//               at a lower level).
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_light_off(Light *light, int priority) {
+  nassertv_always(!is_empty());
+
+  const RenderAttrib *attrib =
+    node()->get_attrib(LightAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    priority = max(priority,
+                   node()->get_state()->get_override(LightAttrib::get_class_type()));
+    const LightAttrib *la = DCAST(LightAttrib, attrib);
+
+    // Modify the existing LightAttrib to add the indicated light
+    // to the "off" list.  This also, incidentally, removes it from
+    // the "on" list if it is there.
+    node()->set_attrib(la->add_off_light(light), priority);
+
+  } else {
+    // Create a new LightAttrib for this node that turns off the
+    // indicated light.
+    CPT(LightAttrib) la = DCAST(LightAttrib, LightAttrib::make());
+    node()->set_attrib(la->add_off_light(light), priority);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_light
+//       Access: Published
+//  Description: Completely removes any lighting operations that may
+//               have been set via set_light() or set_light_off()
+//               from this particular node.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+clear_light() {
+  nassertv_always(!is_empty());
+  node()->clear_attrib(LightAttrib::get_class_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_light
+//       Access: Published
+//  Description: Removes any reference to the indicated light 
+//               from the NodePath.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+clear_light(Light *light) {
+  nassertv_always(!is_empty());
+
+  const RenderAttrib *attrib =
+    node()->get_attrib(LightAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    CPT(LightAttrib) la = DCAST(LightAttrib, attrib);
+    la = DCAST(LightAttrib, la->remove_on_light(light));
+    la = DCAST(LightAttrib, la->remove_off_light(light));
+
+    if (la->is_identity()) {
+      node()->clear_attrib(LightAttrib::get_class_type());
+
+    } else {
+      int priority = node()->get_state()->get_override(LightAttrib::get_class_type());
+      node()->set_attrib(la, priority);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_light
+//       Access: Published
+//  Description: Returns true if the indicated light has been
+//               specifically enabled on this particular node.  This
+//               means that someone called set_light() on this node
+//               with the indicated light.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+has_light(Light *light) const {
+  nassertr_always(!is_empty(), false);
+
+  const RenderAttrib *attrib =
+    node()->get_attrib(LightAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    const LightAttrib *la = DCAST(LightAttrib, attrib);
+    return la->has_on_light(light);
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_light_off
+//       Access: Published
+//  Description: Returns true if all lights have been specifically
+//               disabled on this particular node.  This means that
+//               someone called set_light_off() on this node with no
+//               parameters.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+has_light_off() const {
+  nassertr_always(!is_empty(), false);
+  const RenderAttrib *attrib =
+    node()->get_attrib(LightAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    const LightAttrib *la = DCAST(LightAttrib, attrib);
+    return la->has_all_off();
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_light_off
+//       Access: Published
+//  Description: Returns true if the indicated light has been
+//               specifically disabled on this particular node.  This
+//               means that someone called set_light_off() on this
+//               node with the indicated light.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+has_light_off(Light *light) const {
+  nassertr_always(!is_empty(), false);
+
+  const RenderAttrib *attrib =
+    node()->get_attrib(LightAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    const LightAttrib *la = DCAST(LightAttrib, attrib);
+    return la->has_off_light(light);
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_light
+//       Access: Published
+//  Description: Adds a PolylightEffect to the geometry at this node
+//               and below.  This makes the geometry at this point in
+//               the scene graph grow brighter or dimmer according to
+//               its proximity to the indicated PolylightNode; this is
+//               an effect similar to lighting, but is not related to
+//               traditional hardware T&L.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_light(PolylightNode *) {
+  // Not yet implemented.
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_light
+//       Access: Published
+//  Description: Removes the indicated PolylightEffect from the
+//               geometry.  This undoes a previous set_light() call.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+clear_light(PolylightNode *) {
+  // Not yet implemented.
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_light
+//       Access: Published
+//  Description: Returns true if the indicated PolylightNode was added
+//               to this node, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+has_light(PolylightNode *) {
+  // Not yet implemented.
+  nassertr(false, false);
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_light
+//       Access: Published
+//  Description: Adds a Light or Polylight to the geometry at this
+//               node and below.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_light(const NodePath &light) {
+  if (!light.is_empty()) {
+    PandaNode *node = light.node();
+    Light *light_obj = node->as_light();
+    if (light_obj != (Light *)NULL) {
+      set_light(light_obj);
+      return;
+    }
+  }
+  nassert_raise("Not a light object.");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_light
+//       Access: Published
+//  Description: Removes the indicated PolylightEffect from the
+//               geometry.  This undoes a previous set_light() call.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+clear_light(const NodePath &light) {
+  if (!light.is_empty()) {
+    PandaNode *node = light.node();
+    Light *light_obj = node->as_light();
+    if (light_obj != (Light *)NULL) {
+      clear_light(light_obj);
+      return;
+    }
+  }
+  nassert_raise("Not a light object.");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_light
+//       Access: Published
+//  Description: Returns true if the indicated PolylightNode was added
+//               to this node, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+has_light(const NodePath &light) {
+  if (!light.is_empty()) {
+    PandaNode *node = light.node();
+    Light *light_obj = node->as_light();
+    if (light_obj != (Light *)NULL) {
+      return has_light(light_obj);
+    }
+  }
+  nassert_raise("Not a light object.");
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::set_bin
 //       Access: Published

+ 19 - 0
panda/src/pgraph/nodePath.h

@@ -35,6 +35,8 @@ class NodePathCollection;
 class TextureCollection;
 class FindApproxPath;
 class FindApproxLevelEntry;
+class Light;
+class PolylightNode;
 class Texture;
 class Material;
 class Fog;
@@ -481,6 +483,23 @@ PUBLISHED:
   INLINE float get_sb() const;
   INLINE float get_sa() const;
 
+  void set_light(Light *light, int priority = 0);
+  void set_light_off(int priority = 0);
+  void set_light_off(Light *light, int priority = 0);
+  void clear_light();
+  void clear_light(Light *light);
+  bool has_light(Light *light) const;
+  bool has_light_off() const;
+  bool has_light_off(Light *light) const;
+
+  void set_light(PolylightNode *light);
+  void clear_light(PolylightNode *light);
+  bool has_light(PolylightNode *light);
+
+  void set_light(const NodePath &light);
+  void clear_light(const NodePath &light);
+  bool has_light(const NodePath &light);
+
   void set_bin(const string &bin_name, int draw_order, int priority = 0);
   void clear_bin();
   bool has_bin() const;

+ 23 - 13
panda/src/pgraph/renderState.cxx

@@ -568,6 +568,23 @@ get_attrib(TypeHandle type) const {
   return NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::get_override
+//       Access: Published, Virtual
+//  Description: Looks for a RenderAttrib of the indicated type in the
+//               state, and returns its override value if it is found,
+//               or 0 if it is not.
+////////////////////////////////////////////////////////////////////
+int RenderState::
+get_override(TypeHandle type) const {
+  Attributes::const_iterator ai;
+  ai = _attributes.find(Attribute(type));
+  if (ai != _attributes.end()) {
+    return (*ai)._override;
+  }
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::output
 //       Access: Published, Virtual
@@ -842,11 +859,7 @@ issue_delta_modify(const RenderState *other,
       if ((*ai)._attrib != (*bi)._attrib) {
         any_changed = true;
         (*bi)._attrib->issue(gsg);
-
-      } else if ((*bi)._attrib->always_reissue()) {
-        (*bi)._attrib->issue(gsg);
       }
-        
       *result = *bi;
       ++ai;
       ++bi;
@@ -914,7 +927,7 @@ issue_delta_set(const RenderState *other,
     } else {
       // Here is an attribute we have in both.  Issue the new one if
       // it's different.
-      if ((*ai)._attrib != (*bi)._attrib || (*bi)._attrib->always_reissue()) {
+      if ((*ai)._attrib != (*bi)._attrib) {
         (*bi)._attrib->issue(gsg);
       }
       ++ai;
@@ -1020,20 +1033,17 @@ do_compose(const RenderState *other) const {
       ++bi;
       ++result;
     } else {
-      // Here is an attribute we have in both.  Does one override the
-      // other?
+      // Here is an attribute we have in both.  Does A override B?
       const Attribute &a = (*ai);
       const Attribute &b = (*bi);
-      if (a._override < b._override) {
-        // B overrides.
-        *result = *bi;
-
-      } else if (b._override < a._override) {
+      if (b._override < a._override) {
         // A overrides.
         *result = *ai;
 
       } else {
-        // No, they're equivalent, so compose them.
+        // Either they have the same override value, or B is higher.
+        // In either case, the result is the composition of the two,
+        // with B's override value.
         *result = Attribute(a._attrib->compose(b._attrib), b._override);
       }
       ++ai;

+ 1 - 0
panda/src/pgraph/renderState.h

@@ -89,6 +89,7 @@ PUBLISHED:
   CPT(RenderState) adjust_all_priorities(int adjustment) const;
 
   const RenderAttrib *get_attrib(TypeHandle type) const;
+  int get_override(TypeHandle type) const;
 
   void output(ostream &out) const;
   void write(ostream &out, int indent_level) const;