Explorar el Código

pgraph lighting, optional offset decal implementation

David Rose hace 24 años
padre
commit
783ba052d7
Se han modificado 60 ficheros con 3490 adiciones y 932 borrados
  1. 10 1
      panda/src/collide/collisionSolid.cxx
  2. 77 53
      panda/src/display/graphicsEngine.cxx
  3. 7 4
      panda/src/display/graphicsEngine.h
  4. 22 0
      panda/src/display/graphicsStateGuardian.I
  5. 14 3
      panda/src/display/graphicsStateGuardian.cxx
  6. 8 1
      panda/src/display/graphicsStateGuardian.h
  7. 8 0
      panda/src/dxgsg/dxGraphicsStateGuardian.cxx
  8. 5 0
      panda/src/framework/framework.cxx
  9. 5 0
      panda/src/glgsg/config_glgsg.cxx
  10. 1 0
      panda/src/glgsg/config_glgsg.h
  11. 245 129
      panda/src/glgsg/glGraphicsStateGuardian.cxx
  12. 6 2
      panda/src/glgsg/glGraphicsStateGuardian.h
  13. 11 0
      panda/src/gobj/lens.cxx
  14. 1 0
      panda/src/gobj/lens.h
  15. 5 3
      panda/src/gsgbase/graphicsStateGuardianBase.h
  16. 35 12
      panda/src/light/Sources.pp
  17. 17 0
      panda/src/light/ambientLight.I
  18. 97 15
      panda/src/light/ambientLight.cxx
  19. 41 47
      panda/src/light/ambientLight.h
  20. 12 0
      panda/src/light/config_light.cxx
  21. 92 6
      panda/src/light/directionalLight.I
  22. 155 14
      panda/src/light/directionalLight.cxx
  23. 72 59
      panda/src/light/directionalLight.h
  24. 98 0
      panda/src/light/light.I
  25. 100 2
      panda/src/light/light.cxx
  26. 74 39
      panda/src/light/light.h
  27. 88 0
      panda/src/light/lightAttrib.I
  28. 489 0
      panda/src/light/lightAttrib.cxx
  29. 116 0
      panda/src/light/lightAttrib.h
  30. 17 0
      panda/src/light/lightLensNode.I
  31. 115 0
      panda/src/light/lightLensNode.cxx
  32. 81 0
      panda/src/light/lightLensNode.h
  33. 17 0
      panda/src/light/lightNode.I
  34. 115 0
      panda/src/light/lightNode.cxx
  35. 83 0
      panda/src/light/lightNode.h
  36. 3 1
      panda/src/light/light_composite1.cxx
  37. 0 1
      panda/src/light/light_composite2.cxx
  38. 63 39
      panda/src/light/pointLight.I
  39. 153 17
      panda/src/light/pointLight.cxx
  40. 72 71
      panda/src/light/pointLight.h
  41. 68 59
      panda/src/light/spotlight.I
  42. 223 154
      panda/src/light/spotlight.cxx
  43. 90 83
      panda/src/light/spotlight.h
  44. 3 0
      panda/src/pgraph/Sources.pp
  45. 1 3
      panda/src/pgraph/cullHandler.cxx
  46. 1 0
      panda/src/pgraph/cullTraverserData.cxx
  47. 14 1
      panda/src/pgraph/pandaNode.cxx
  48. 2 0
      panda/src/pgraph/pandaNode.h
  49. 78 56
      panda/src/pgraph/qpcullTraverser.I
  50. 51 14
      panda/src/pgraph/qpcullTraverser.cxx
  51. 16 12
      panda/src/pgraph/qpcullTraverser.h
  52. 1 24
      panda/src/pgraph/qplensNode.I
  53. 36 0
      panda/src/pgraph/qplensNode.cxx
  54. 4 3
      panda/src/pgraph/qplensNode.h
  55. 1 0
      panda/src/pgraph/renderState.cxx
  56. 145 0
      panda/src/pgraph/sceneSetup.I
  57. 20 0
      panda/src/pgraph/sceneSetup.cxx
  58. 66 0
      panda/src/pgraph/sceneSetup.h
  59. 0 3
      panda/src/testbed/demo.cxx
  60. 40 1
      panda/src/testbed/pview.cxx

+ 10 - 1
panda/src/collide/collisionSolid.cxx

@@ -100,7 +100,9 @@ update_viz(Node *parent) {
 //     Function: CollisionSolid::get_viz
 //       Access: Public
 //  Description: Returns a GeomNode that may be rendered to visualize
-//               the CollisionSolid.
+//               the CollisionSolid.  This is used during the cull
+//               traversal to render the CollisionNodes that have been
+//               made visible.
 ////////////////////////////////////////////////////////////////////
 qpGeomNode *CollisionSolid::
 get_viz() {
@@ -386,6 +388,7 @@ get_solid_viz_state() {
       (CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise),
        RenderModeAttrib::make(RenderModeAttrib::M_filled),
        TransparencyAttrib::make(TransparencyAttrib::M_alpha));
+    base_state->ref();  // once more to guard against static destruction
   }
 
   if (_tangible) {
@@ -393,6 +396,7 @@ get_solid_viz_state() {
     if (tangible_state == (const RenderState *)NULL) {
       tangible_state = base_state->add_attrib
         (ColorAttrib::make_flat(Colorf(1.0f, 1.0f, 1.0f, 0.5f)));
+      tangible_state->ref();
     }
     return tangible_state;
 
@@ -401,6 +405,7 @@ get_solid_viz_state() {
     if (intangible_state == (const RenderState *)NULL) {
       intangible_state = base_state->add_attrib
         (ColorAttrib::make_flat(Colorf(1.0f, 0.3f, 0.5f, 0.5f)));
+      intangible_state->ref();
     }
     return intangible_state;
   }
@@ -425,6 +430,7 @@ get_wireframe_viz_state() {
       (CullFaceAttrib::make(CullFaceAttrib::M_cull_none),
        RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
        TransparencyAttrib::make(TransparencyAttrib::M_none));
+    base_state->ref();  // once more to guard against static destruction
   }
 
   if (_tangible) {
@@ -432,6 +438,7 @@ get_wireframe_viz_state() {
     if (tangible_state == (const RenderState *)NULL) {
       tangible_state = base_state->add_attrib
         (ColorAttrib::make_flat(Colorf(0.0f, 0.0f, 1.0f, 1.0f)));
+      tangible_state->ref();
     }
     return tangible_state;
 
@@ -440,6 +447,7 @@ get_wireframe_viz_state() {
     if (intangible_state == (const RenderState *)NULL) {
       intangible_state = base_state->add_attrib
         (ColorAttrib::make_flat(Colorf(1.0f, 1.0f, 0.0f, 1.0f)));
+      intangible_state->ref();
     }
     return intangible_state;
   }
@@ -463,6 +471,7 @@ get_other_viz_state() {
       (CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise),
        RenderModeAttrib::make(RenderModeAttrib::M_filled),
        TransparencyAttrib::make(TransparencyAttrib::M_alpha));
+    base_state->ref();  // once more to guard against static destruction
   }
 
   // We don't bother to make a distinction here between tangible and

+ 77 - 53
panda/src/display/graphicsEngine.cxx

@@ -111,6 +111,7 @@ cull_and_draw_together() {
   Windows::iterator wi;
   for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
     GraphicsWindow *win = (*wi);
+    win->get_gsg()->reset_frame();
     win->clear();
 
     int num_display_regions = win->get_num_display_regions();
@@ -135,12 +136,13 @@ cull_and_draw_together(GraphicsWindow *win, DisplayRegion *dr) {
   GraphicsStateGuardian *gsg = win->get_gsg();
   nassertv(gsg != (GraphicsStateGuardian *)NULL);
 
-  if (set_gsg_lens(gsg, dr)) {
+  PT(SceneSetup) scene_setup = setup_scene(dr->get_qpcamera(), gsg);
+  if (setup_gsg(gsg, scene_setup)) {
     DisplayRegionStack old_dr = gsg->push_display_region(dr);
     gsg->prepare_display_region();
     
     DrawCullHandler cull_handler(gsg);
-    do_cull(&cull_handler, dr->get_qpcamera(), gsg);
+    do_cull(&cull_handler, scene_setup, gsg);
     
     gsg->pop_display_region(old_dr);
   }
@@ -158,6 +160,7 @@ cull_bin_draw() {
   Windows::iterator wi;
   for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
     GraphicsWindow *win = (*wi);
+    win->get_gsg()->reset_frame();
     win->clear();
 
     int num_display_regions = win->get_num_display_regions();
@@ -187,64 +190,64 @@ cull_bin_draw(GraphicsWindow *win, DisplayRegion *dr) {
     cull_result = new CullResult(gsg);
   }
 
-  BinCullHandler cull_handler(cull_result);
-  do_cull(&cull_handler, dr->get_qpcamera(), gsg);
-
-  cull_result->finish_cull();
-
-  // Save the results for next frame.
-  dr->_cull_result = cull_result->make_next();
-
-  // Now draw.
-  do_draw(cull_result, gsg, dr);
+  PT(SceneSetup) scene_setup = setup_scene(dr->get_qpcamera(), gsg);
+  if (scene_setup != (SceneSetup *)NULL) {
+    BinCullHandler cull_handler(cull_result);
+    do_cull(&cull_handler, scene_setup, gsg);
+    
+    cull_result->finish_cull();
+    
+    // Save the results for next frame.
+    dr->_cull_result = cull_result->make_next();
+    
+    // Now draw.
+    do_draw(cull_result, scene_setup, gsg, dr);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::do_cull
+//     Function: GraphicsEngine::setup_scene
 //       Access: Private
-//  Description: Fires off a cull traversal using the indicated camera.
+//  Description: Returns a new SceneSetup object appropriate for
+//               rendering the scene from the indicated camera, or
+//               NULL if the scene should not be rendered for some
+//               reason.
 ////////////////////////////////////////////////////////////////////
-void GraphicsEngine::
-do_cull(CullHandler *cull_handler, const qpNodePath &camera,
-        GraphicsStateGuardian *gsg) {
-  // Statistics
-  PStatTimer timer(_cull_pcollector);
-
+PT(SceneSetup) GraphicsEngine::
+setup_scene(const qpNodePath &camera, GraphicsStateGuardian *gsg) {
   if (camera.is_empty()) {
     // No camera, no draw.
-    return;
+    return NULL;
   }
 
   qpCamera *camera_node;
-  DCAST_INTO_V(camera_node, camera.node());
+  DCAST_INTO_R(camera_node, camera.node(), NULL);
 
   if (!camera_node->is_active()) {
     // Camera inactive, no draw.
-    return;
+    return NULL;
   }
 
   Lens *lens = camera_node->get_lens();
   if (lens == (Lens *)NULL) {
     // No lens, no draw.
-    return;
+    return NULL;
   }
 
-  qpNodePath scene = camera_node->get_scene();
-  if (scene.is_empty()) {
+  qpNodePath scene_root = camera_node->get_scene();
+  if (scene_root.is_empty()) {
     // No scene, no draw.
-    return;
+    return NULL;
   }
 
-  qpCullTraverser trav;
-  trav.set_cull_handler(cull_handler);
-  trav.set_camera_mask(camera_node->get_camera_mask());
+  PT(SceneSetup) scene_setup = new SceneSetup;
 
   // We will need both the camera transform (the net transform to the
   // camera from the scene) and the world transform (the camera
   // transform inverse, or the net transform to the scene from the
   // camera).
-  CPT(TransformState) camera_transform = camera.get_transform(scene);
-  CPT(TransformState) world_transform = scene.get_transform(camera);
+  CPT(TransformState) camera_transform = camera.get_transform(scene_root);
+  CPT(TransformState) world_transform = scene_root.get_transform(camera);
 
   // The render transform is the same as the world transform, except
   // it is converted into the GSG's internal coordinate system.  This
@@ -259,8 +262,31 @@ do_cull(CullHandler *cull_handler, const qpNodePath &camera,
     render_transform = cs_transform->compose(render_transform);
   }
 
-  trav.set_camera_transform(camera_transform);
-  trav.set_render_transform(render_transform);
+  scene_setup->set_scene_root(scene_root);
+  scene_setup->set_camera(camera_node);
+  scene_setup->set_lens(lens);
+  scene_setup->set_camera_transform(camera_transform);
+  scene_setup->set_render_transform(render_transform);
+
+  return scene_setup;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::do_cull
+//       Access: Private
+//  Description: Fires off a cull traversal using the indicated camera.
+////////////////////////////////////////////////////////////////////
+void GraphicsEngine::
+do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
+        GraphicsStateGuardian *gsg) {
+  // Statistics
+  PStatTimer timer(_cull_pcollector);
+
+  qpCullTraverser trav;
+  trav.set_cull_handler(cull_handler);
+  trav.set_depth_offset_decals(gsg->depth_offset_decals());
+  trav.set_scene(scene_setup);
+  trav.set_camera_mask(scene_setup->get_camera()->get_camera_mask());
 
   if (qpview_frustum_cull) {
     // If we're to be performing view-frustum culling, determine the
@@ -268,20 +294,20 @@ do_cull(CullHandler *cull_handler, const qpNodePath &camera,
 
     // First, we have to get the current viewing frustum, which comes
     // from the lens.
-    PT(BoundingVolume) bv = lens->make_bounds();
+    PT(BoundingVolume) bv = scene_setup->get_lens()->make_bounds();
 
     if (bv != (BoundingVolume *)NULL &&
         bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
       // Transform it into the appropriate coordinate space.
       PT(GeometricBoundingVolume) local_frustum;
       local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy());
-      local_frustum->xform(camera_transform->get_mat());
+      local_frustum->xform(scene_setup->get_camera_transform()->get_mat());
 
       trav.set_view_frustum(local_frustum);
     }
   }
   
-  trav.traverse(scene);
+  trav.traverse(scene_setup->get_scene_root());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -290,12 +316,12 @@ do_cull(CullHandler *cull_handler, const qpNodePath &camera,
 //  Description: Draws the previously-culled scene.
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
-do_draw(CullResult *cull_result, GraphicsStateGuardian *gsg,
-        DisplayRegion *dr) {
+do_draw(CullResult *cull_result, SceneSetup *scene_setup,
+        GraphicsStateGuardian *gsg, DisplayRegion *dr) {
   // Statistics
   PStatTimer timer(_draw_pcollector);
 
-  if (set_gsg_lens(gsg, dr)) {
+  if (setup_gsg(gsg, scene_setup)) {
     DisplayRegionStack old_dr = gsg->push_display_region(dr);
     gsg->prepare_display_region();
     cull_result->draw();
@@ -304,25 +330,21 @@ do_draw(CullResult *cull_result, GraphicsStateGuardian *gsg,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::set_gsg_lens
+//     Function: GraphicsEngine::setup_gsg
 //       Access: Private
-//  Description: Sets up the GSG to draw with the lens from the
-//               indicated DisplayRegion.  Returns true if the lens is
-//               acceptable, false otherwise.
+//  Description: Sets up the GSG to draw the indicated scene.  Returns
+//               true if the scene (and its lens) is acceptable, false
+//               otherwise.
 ////////////////////////////////////////////////////////////////////
 bool GraphicsEngine::
-set_gsg_lens(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
-  const qpNodePath &camera = dr->get_qpcamera();
-  if (camera.is_empty()) {
-    // No camera, no draw.
+setup_gsg(GraphicsStateGuardian *gsg, SceneSetup *scene_setup) {
+  if (scene_setup == (SceneSetup *)NULL) {
+    // No scene, no draw.
     return false;
   }
 
-  qpCamera *camera_node;
-  DCAST_INTO_R(camera_node, camera.node(), false);
-
-  Lens *lens = camera_node->get_lens();
-  if (lens == (Lens *)NULL) {
+  const Lens *lens = scene_setup->get_lens();
+  if (lens == (const Lens *)NULL) {
     // No lens, no draw.
     return false;
   }
@@ -335,5 +357,7 @@ set_gsg_lens(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
     return false;
   }
 
+  gsg->set_scene(scene_setup);
+
   return true;
 }

+ 7 - 4
panda/src/display/graphicsEngine.h

@@ -21,6 +21,7 @@
 
 #include "pandabase.h"
 #include "graphicsWindow.h"
+#include "sceneSetup.h"
 #include "pointerTo.h"
 #include "pset.h"
 #include "pStatCollector.h"
@@ -58,12 +59,14 @@ private:
   void cull_bin_draw();
   void cull_bin_draw(GraphicsWindow *win, DisplayRegion *dr);
 
-  void do_cull(CullHandler *cull_handler, const qpNodePath &camera,
+  PT(SceneSetup) setup_scene(const qpNodePath &camera, 
+                             GraphicsStateGuardian *gsg);
+  void do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
                GraphicsStateGuardian *gsg);
-  void do_draw(CullResult *cull_result, GraphicsStateGuardian *gsg,
-               DisplayRegion *dr);
+  void do_draw(CullResult *cull_result, SceneSetup *scene_setup,
+               GraphicsStateGuardian *gsg, DisplayRegion *dr);
 
-  bool set_gsg_lens(GraphicsStateGuardian *gsg, DisplayRegion *dr);
+  bool setup_gsg(GraphicsStateGuardian *gsg, SceneSetup *scene_setup);
 
   Pipeline *_pipeline;
 

+ 22 - 0
panda/src/display/graphicsStateGuardian.I

@@ -96,6 +96,28 @@ is_closed() const {
   return (_win == (GraphicsWindow *)NULL);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::set_scene
+//       Access: Public
+//  Description: Sets the SceneSetup object that indicates the initial
+//               camera position, etc.  This must be called before
+//               traversal begins.
+////////////////////////////////////////////////////////////////////
+INLINE void GraphicsStateGuardian::
+set_scene(SceneSetup *scene_setup) {
+  _scene_setup = scene_setup;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_scene
+//       Access: Public
+//  Description: Returns the SceneSetup object.
+////////////////////////////////////////////////////////////////////
+INLINE SceneSetup *GraphicsStateGuardian::
+get_scene() const {
+  return _scene_setup;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::set_state
 //       Access: Public

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

@@ -836,6 +836,17 @@ prepare_lens() {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::reset_frame
+//       Access: Public, Virtual
+//  Description: Called before each frame is rendered, to allow the
+//               GSG a chance to do any internal cleanup before
+//               beginning the frame.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+reset_frame() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::wants_normals
 //       Access: Public, Virtual
@@ -899,15 +910,15 @@ end_decal(GeomNode *) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::polygon_offset_decals
+//     Function: GraphicsStateGuardian::depth_offset_decals
 //       Access: Public, Virtual
 //  Description: Returns true if this GSG can implement decals using a
-//               PolygonOffsetAttrib, or false if that is unreliable
+//               DepthOffsetAttrib, or false if that is unreliable
 //               and the three-step rendering process should be used
 //               instead.
 ////////////////////////////////////////////////////////////////////
 bool GraphicsStateGuardian::
-polygon_offset_decals() {
+depth_offset_decals() {
   return false;
 }
 

+ 8 - 1
panda/src/display/graphicsStateGuardian.h

@@ -27,6 +27,7 @@
 #include "lensStack.h"
 
 #include "graphicsStateGuardianBase.h"
+#include "sceneSetup.h"
 #include "nodeTransition.h"
 #include "nodeTransitionCache.h"
 #include "luse.h"
@@ -92,6 +93,9 @@ PUBLISHED:
 public:
   INLINE bool is_closed() const;
 
+  INLINE void set_scene(SceneSetup *scene_setup);
+  INLINE SceneSetup *get_scene() const;
+
   virtual TextureContext *prepare_texture(Texture *tex);
   virtual void apply_texture(TextureContext *tc);
   virtual void release_texture(TextureContext *tc);
@@ -124,6 +128,7 @@ public:
 
   INLINE void enable_normals(bool val) { _normals_enabled = val; }
 
+  virtual void reset_frame();
 
   // These functions will be queried by the GeomIssuer to determine if
   // it should issue normals, texcoords, and/or colors, based on the
@@ -135,7 +140,7 @@ public:
   virtual void begin_decal(GeomNode *base_geom, AllTransitionsWrapper &attrib);
   virtual void end_decal(GeomNode *base_geom);
 
-  virtual bool polygon_offset_decals();
+  virtual bool depth_offset_decals();
   virtual CPT(RenderState) begin_decal_base_first();
   virtual CPT(RenderState) begin_decal_nested();
   virtual CPT(RenderState) begin_decal_base_second();
@@ -217,6 +222,8 @@ protected:
 #endif
 
 protected:
+  PT(SceneSetup) _scene_setup;
+
   class StateInfo {
   public:
     INLINE StateInfo(TypeHandle type);

+ 8 - 0
panda/src/dxgsg/dxGraphicsStateGuardian.cxx

@@ -4757,6 +4757,7 @@ apply_fog(Fog *fog) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian::apply_light( PointLight* light ) {
+#if 0
     // The light position will be relative to the current matrix, so
     // we have to know what the current matrix is.  Find a better
     // solution later.
@@ -4780,6 +4781,7 @@ void DXGraphicsStateGuardian::apply_light( PointLight* light ) {
     alight.dvAttenuation2 = (D3DVALUE)light->get_quadratic_attenuation();
     
     HRESULT res = scrn.pD3DDevice->SetLight(_cur_light_id, &alight);
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -4788,6 +4790,7 @@ void DXGraphicsStateGuardian::apply_light( PointLight* light ) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian::apply_light( DirectionalLight* light ) {
+#if 0
     // The light position will be relative to the current matrix, so
     // we have to know what the current matrix is.  Find a better
     // solution later.
@@ -4815,6 +4818,7 @@ void DXGraphicsStateGuardian::apply_light( DirectionalLight* light ) {
     HRESULT res = scrn.pD3DDevice->SetLight(_cur_light_id, &alight);
 //    scrn.pD3DDevice->LightEnable( _cur_light_id, TRUE );
 //    scrn.pD3DDevice->SetRenderState( D3DRENDERSTATE_LIGHTING, TRUE );
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -4823,6 +4827,7 @@ void DXGraphicsStateGuardian::apply_light( DirectionalLight* light ) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian::apply_light( Spotlight* light ) {
+#if 0
     // The light position will be relative to the current matrix, so
     // we have to know what the current matrix is.  Find a better
     // solution later.
@@ -4900,6 +4905,7 @@ void DXGraphicsStateGuardian::apply_light( Spotlight* light ) {
     HRESULT res = scrn.pD3DDevice->SetLight(_cur_light_id, &alight);
 
 #endif              // WBD_GL_MODE
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -5194,6 +5200,7 @@ issue_render_mode(const RenderModeTransition *attrib) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian::issue_light(const LightTransition *attrib ) {
+#if 0
   nassertv(attrib->get_default_dir() != TD_on);
 
   // Initialize the current ambient light total and currently enabled
@@ -5285,6 +5292,7 @@ void DXGraphicsStateGuardian::issue_light(const LightTransition *attrib ) {
     call_dxLightModelAmbient(_cur_ambient_light);
     enable_color_material(true);
   }
+#endif
 }
 
 /*

+ 5 - 0
panda/src/framework/framework.cxx

@@ -325,6 +325,8 @@ void display_func( void ) {
 
 void set_lighting(bool enabled) {
   if (enabled) {
+    cerr << "lighting is no longer supported in old scene graph.\n";
+    /*
     // Enable the lights on the initial state.
     PT(LightTransition) la = new LightTransition;
     la->set_on(light.p());
@@ -333,6 +335,7 @@ void set_lighting(bool enabled) {
       la->set_on(dlight.p());
     }
     render_arc->set_transition(la);
+    */
 
   } else {
     // Remove the lights from the initial state.
@@ -1081,6 +1084,7 @@ int framework_main(int argc, char *argv[]) {
   lights = new NamedNode("lights");
   new RenderRelation(cameras, lights);
 
+  /*
   light = new AmbientLight( "ambient" );
   dlight = new DirectionalLight( "directional" );
   plight = new PointLight( "point" );
@@ -1094,6 +1098,7 @@ int framework_main(int argc, char *argv[]) {
   new RenderRelation( lights, plight );
   new RenderRelation( lights, slight );
 #endif
+  */
 
   // Turn on culling.
   CullFaceTransition *cfa = new CullFaceTransition(CullFaceProperty::M_cull_clockwise);

+ 5 - 0
panda/src/glgsg/config_glgsg.cxx

@@ -65,6 +65,11 @@ bool gl_save_mipmaps = config_glgsg.GetBool("gl-save-mipmaps", false);
 // variable.
 bool gl_auto_normalize_lighting = config_glgsg.GetBool("auto-normalize-lighting", false);
 
+// Configure this true to try to implement decals using a
+// DepthOffsetAttrib, false to do them with the more reliable 3-pass
+// rendering method instead.
+bool gl_depth_offset_decals = config_glgsg.GetBool("depth-offset-decals", false);
+
 // Configure this true to indicate the current version of GL fully
 // supports textures with B, G, R ordering; false if it only supports
 // R, G, B.  false will always work, but true might be faster if the

+ 1 - 0
panda/src/glgsg/config_glgsg.h

@@ -32,6 +32,7 @@ extern bool gl_force_mipmaps;
 extern bool gl_show_mipmaps;
 extern bool gl_save_mipmaps;
 extern bool gl_auto_normalize_lighting;
+extern bool gl_depth_offset_decals;
 extern bool gl_supports_bgr;
 
 // Ways to implement decals.

+ 245 - 129
panda/src/glgsg/glGraphicsStateGuardian.cxx

@@ -66,6 +66,7 @@
 #include "pointShapeTransition.h"
 #include "polygonOffsetTransition.h"
 #include "textureAttrib.h"
+#include "lightAttrib.h"
 #include "cullFaceAttrib.h"
 #include "transparencyAttrib.h"
 #include "depthTestAttrib.h"
@@ -327,6 +328,9 @@ reset() {
   la->issue(this);
   ta->issue(this);
 
+  Material empty;
+  apply_material(&empty);
+
   if (gl_cheap_textures) {
     glgsg_cat.info()
       << "Setting glHint() for fastest textures.\n";
@@ -1019,16 +1023,9 @@ draw_sprite(GeomSprite *geom, GeomContext *) {
   // save the modelview matrix
   const LMatrix4f &modelview_mat = _transform->get_mat();
 
-  // get the camera information
-
-  // Hmm, this doesn't work any more, since we don't store the camera
-  // pointer in new scene graph land.  Need to find a better way to
-  // get the current window's aspect ratio.  Here's a temporary hack
-  // for now.
-
-  //  float aspect_ratio = 
-  //    get_current_camera()->get_lens()->get_aspect_ratio();
-  float aspect_ratio = 1.333333;
+  // We don't need to mess with the aspect ratio, since we are now
+  // using the default projection matrix, which has the right aspect
+  // ratio built in.
 
   // load up our own matrices
   glMatrixMode(GL_MODELVIEW);
@@ -1088,7 +1085,7 @@ draw_sprite(GeomSprite *geom, GeomContext *) {
 
   // y direction
   if (y_overall)
-    scaled_height = geom->_y_texel_ratio[0] * half_height * aspect_ratio;
+    scaled_height = geom->_y_texel_ratio[0] * half_height;
   else {
     nassertv(((int)geom->_y_texel_ratio.size() >= geom->get_num_prims()));
     y_walk = &geom->_y_texel_ratio[0];
@@ -1170,7 +1167,7 @@ draw_sprite(GeomSprite *geom, GeomContext *) {
       scaled_width = cur_image._x_ratio * half_width;
 
     if (y_overall == false)
-      scaled_height = cur_image._y_ratio * half_height * aspect_ratio;
+      scaled_height = cur_image._y_ratio * half_height;
 
     // if not G_OVERALL, do some trig for this z rotate
     if (theta_on) {
@@ -2526,58 +2523,48 @@ apply_fog(qpFog *fog) {
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-void GLGraphicsStateGuardian::apply_light( PointLight* light )
-{
-  // The light position will be relative to the current matrix, so
-  // we have to know what the current matrix is.  Find a better
-  // solution later.
-#ifdef GSG_VERBOSE
-  glgsg_cat.debug()
-    << "glMatrixMode(GL_MODELVIEW)" << endl;
-  glgsg_cat.debug()
-    << "glPushMatrix()" << endl;
-  glgsg_cat.debug()
-    << "glLoadIdentity()" << endl;
-#endif
+void GLGraphicsStateGuardian::
+apply_light(PointLight *light) {
+  // We need to temporarily load a new matrix so we can define the
+  // light in a known coordinate system.  We pick the transform of the
+  // root.  (Alternatively, we could leave the current transform where
+  // it is and compute the light position relative to that transform
+  // instead of relative to the root, by composing with the matrix
+  // computed by _transform->invert_compose(render_transform).  But I
+  // think loading a completely new matrix is simpler.)
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
+  glLoadMatrixf(_scene_setup->get_render_transform()->get_mat().get_data());
 
-  glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_right)
-                .get_data());
-
-  GLenum id = get_light_id( _cur_light_id );
-  Colorf black(0, 0, 0, 1);
+  GLenum id = get_light_id(_cur_light_id);
+  static const Colorf black(0.0f, 0.0f, 0.0f, 1.0f);
   glLightfv(id, GL_AMBIENT, black.get_data());
   glLightfv(id, GL_DIFFUSE, light->get_color().get_data());
-  glLightfv(id, GL_SPECULAR, light->get_specular().get_data());
+  glLightfv(id, GL_SPECULAR, light->get_specular_color().get_data());
+
+  // Position needs to specify x, y, z, and w
+  // w == 1 implies non-infinite position
+  qpNodePath light_np(light);
+  const LMatrix4f &light_mat = light_np.get_mat(qpNodePath());
+  LPoint3f pos = light->get_point() * light_mat;
 
-    // Position needs to specify x, y, z, and w
-    // w == 1 implies non-infinite position
-  LPoint3f pos = get_rel_pos( light, _current_camera );
-  LPoint4f fpos( pos[0], pos[1], pos[2], 1 );
-  glLightfv( id, GL_POSITION, fpos.get_data() );
+  LPoint4f fpos(pos[0], pos[1], pos[2], 1.0f);
+  glLightfv(id, GL_POSITION, fpos.get_data());
 
   // GL_SPOT_DIRECTION is not significant when cutoff == 180
 
-    // Exponent == 0 implies uniform light distribution
-  glLightf( id, GL_SPOT_EXPONENT, 0 );
+  // Exponent == 0 implies uniform light distribution
+  glLightf(id, GL_SPOT_EXPONENT, 0.0f);
 
   // Cutoff == 180 means uniform point light source
-  glLightf( id, GL_SPOT_CUTOFF, 180.0 );
+  glLightf(id, GL_SPOT_CUTOFF, 180.0f);
 
-  glLightf( id, GL_CONSTANT_ATTENUATION,
-            light->get_constant_attenuation() );
-  glLightf( id, GL_LINEAR_ATTENUATION,
-            light->get_linear_attenuation() );
-  glLightf( id, GL_QUADRATIC_ATTENUATION,
-            light->get_quadratic_attenuation() );
+  const LVecBase3f &att = light->get_attenuation();
+  glLightf(id, GL_CONSTANT_ATTENUATION, att[0]);
+  glLightf(id, GL_LINEAR_ATTENUATION, att[1]);
+  glLightf(id, GL_QUADRATIC_ATTENUATION, att[2]);
 
   glPopMatrix();
-
-#ifdef GSG_VERBOSE
-  glgsg_cat.debug()
-    << "glPopMatrix()" << endl;
-#endif
   report_errors();
 }
 
@@ -2586,56 +2573,43 @@ void GLGraphicsStateGuardian::apply_light( PointLight* light )
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-void GLGraphicsStateGuardian::apply_light( DirectionalLight* light )
-{
-  // The light position will be relative to the current matrix, so
-  // we have to know what the current matrix is.  Find a better
-  // solution later.
-#ifdef GSG_VERBOSE
-  glgsg_cat.debug()
-    << "glMatrixMode(GL_MODELVIEW)" << endl;
-  glgsg_cat.debug()
-    << "glPushMatrix()" << endl;
-  glgsg_cat.debug()
-    << "glLoadIdentity()" << endl;
-#endif
+void GLGraphicsStateGuardian::
+apply_light(DirectionalLight *light) {
+  // See the comment about this transform change in PointLight, above.
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
-  glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_right)
-                .get_data());
+  glLoadMatrixf(_scene_setup->get_render_transform()->get_mat().get_data());
 
   GLenum id = get_light_id( _cur_light_id );
-  Colorf black(0, 0, 0, 1);
+  static const Colorf black(0.0f, 0.0f, 0.0f, 1.0f);
   glLightfv(id, GL_AMBIENT, black.get_data());
   glLightfv(id, GL_DIFFUSE, light->get_color().get_data());
-  glLightfv(id, GL_SPECULAR, light->get_specular().get_data());
+  glLightfv(id, GL_SPECULAR, light->get_specular_color().get_data());
 
-    // Position needs to specify x, y, z, and w
-    // w == 0 implies light is at infinity
-  LPoint3f dir = get_rel_forward( light, _current_camera,
-                                  _coordinate_system );
-  LPoint4f pos( -dir[0], -dir[1], -dir[2], 0 );
-  glLightfv( id, GL_POSITION, pos.get_data() );
+  // Position needs to specify x, y, z, and w.
+  // w == 0 implies light is at infinity
+  qpNodePath light_np(light);
+  const LMatrix4f &light_mat = light_np.get_mat(qpNodePath());
+  LVector3f dir = light->get_direction() * light_mat;
+  LPoint4f fdir(-dir[0], -dir[1], -dir[2], 0);
+  glLightfv(id, GL_POSITION, fdir.get_data());
 
   // GL_SPOT_DIRECTION is not significant when cutoff == 180
   // In this case, position x, y, z specifies direction
 
   // Exponent == 0 implies uniform light distribution
-  glLightf( id, GL_SPOT_EXPONENT, 0 );
+  glLightf(id, GL_SPOT_EXPONENT, 0.0f);
 
   // Cutoff == 180 means uniform point light source
-  glLightf( id, GL_SPOT_CUTOFF, 180.0 );
+  glLightf(id, GL_SPOT_CUTOFF, 180.0f);
 
-  // Default attenuation values (only spotlight can modify these)
-  glLightf( id, GL_CONSTANT_ATTENUATION, 1 );
-  glLightf( id, GL_LINEAR_ATTENUATION, 0 );
-  glLightf( id, GL_QUADRATIC_ATTENUATION, 0 );
+  // Default attenuation values (only spotlight and point light can
+  // modify these)
+  glLightf(id, GL_CONSTANT_ATTENUATION, 1.0f);
+  glLightf(id, GL_LINEAR_ATTENUATION, 0.0f);
+  glLightf(id, GL_QUADRATIC_ATTENUATION, 0.0f);
 
   glPopMatrix();
-#ifdef GSG_VERBOSE
-  glgsg_cat.debug()
-    << "glPopMatrix()" << endl;
-#endif
   report_errors();
 }
 
@@ -2644,54 +2618,42 @@ void GLGraphicsStateGuardian::apply_light( DirectionalLight* light )
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-void GLGraphicsStateGuardian::apply_light( Spotlight* light )
-{
-  // The light position will be relative to the current matrix, so
-  // we have to know what the current matrix is.  Find a better
-  // solution later.
-#ifdef GSG_VERBOSE
-  glgsg_cat.debug()
-    << "glMatrixMode(GL_MODELVIEW)" << endl;
-  glgsg_cat.debug()
-    << "glPushMatrix()" << endl;
-  glgsg_cat.debug()
-    << "glLoadIdentity()" << endl;
-#endif
+void GLGraphicsStateGuardian::
+apply_light(Spotlight *light) {
+  Lens *lens = light->get_lens();
+  nassertv(lens != (Lens *)NULL);
+
+  // See the comment about this transform change in PointLight, above.
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
-  glLoadMatrixf(LMatrix4f::convert_mat(_coordinate_system, CS_yup_right)
-                .get_data());
+  glLoadMatrixf(_scene_setup->get_render_transform()->get_mat().get_data());
 
-  GLenum id = get_light_id( _cur_light_id );
-  Colorf black(0, 0, 0, 1);
+  GLenum id = get_light_id(_cur_light_id);
+  static const Colorf black(0.0f, 0.0f, 0.0f, 1.0f);
   glLightfv(id, GL_AMBIENT, black.get_data());
   glLightfv(id, GL_DIFFUSE, light->get_color().get_data());
-  glLightfv(id, GL_SPECULAR, light->get_specular().get_data());
-
-    // Position needs to specify x, y, z, and w
-    // w == 1 implies non-infinite position
-  LPoint3f pos = get_rel_pos( light, _current_camera );
-  LPoint4f fpos( pos[0], pos[1], pos[2], 1 );
-  glLightfv( id, GL_POSITION, fpos.get_data() );
-
-  glLightfv( id, GL_SPOT_DIRECTION,
-             get_rel_forward( light, _current_camera,
-                              _coordinate_system ).get_data() );
-  glLightf( id, GL_SPOT_EXPONENT, light->get_exponent() );
-  glLightf( id, GL_SPOT_CUTOFF,
-            light->get_cutoff_angle() );
-  glLightf( id, GL_CONSTANT_ATTENUATION,
-            light->get_constant_attenuation() );
-  glLightf( id, GL_LINEAR_ATTENUATION,
-            light->get_linear_attenuation() );
-  glLightf( id, GL_QUADRATIC_ATTENUATION,
-            light->get_quadratic_attenuation() );
+  glLightfv(id, GL_SPECULAR, light->get_specular_color().get_data());
+
+  // Position needs to specify x, y, z, and w
+  // w == 1 implies non-infinite position
+  qpNodePath light_np(light);
+  const LMatrix4f &light_mat = light_np.get_mat(qpNodePath());
+  LPoint3f pos = lens->get_nodal_point() * light_mat;
+  LVector3f dir = lens->get_view_vector() * light_mat;
+
+  LPoint4f fpos(pos[0], pos[1], pos[2], 1.0f);
+  glLightfv(id, GL_POSITION, fpos.get_data());
+  glLightfv(id, GL_SPOT_DIRECTION, dir.get_data());
+
+  glLightf(id, GL_SPOT_EXPONENT, light->get_exponent());
+  glLightf(id, GL_SPOT_CUTOFF, lens->get_hfov());
+
+  const LVecBase3f &att = light->get_attenuation();
+  glLightf(id, GL_CONSTANT_ATTENUATION, att[0]);
+  glLightf(id, GL_LINEAR_ATTENUATION, att[1]);
+  glLightf(id, GL_QUADRATIC_ATTENUATION, att[2]);
 
   glPopMatrix();
-#ifdef GSG_VERBOSE
-  glgsg_cat.debug()
-    << "glPopMatrix()" << endl;
-#endif
   report_errors();
 }
 
@@ -2700,8 +2662,8 @@ void GLGraphicsStateGuardian::apply_light( Spotlight* light )
 //       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-void GLGraphicsStateGuardian::apply_light( AmbientLight* )
-{
+void GLGraphicsStateGuardian::
+apply_light(AmbientLight *) {
   // Ambient lights are handled as a special case in issue_light().
 }
 
@@ -2984,6 +2946,7 @@ issue_render_mode(const RenderModeTransition *attrib) {
 ////////////////////////////////////////////////////////////////////
 void GLGraphicsStateGuardian::issue_light(const LightTransition *attrib )
 {
+#if 0
   nassertv(attrib->get_default_dir() != TD_on);
   //  activate();
 
@@ -3075,6 +3038,7 @@ void GLGraphicsStateGuardian::issue_light(const LightTransition *attrib )
     call_glLightModelAmbient(cur_ambient_light);
   }
   report_errors();
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -3491,6 +3455,104 @@ issue_texture(const TextureAttrib *attrib) {
   report_errors();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::issue_light
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+issue_light(const LightAttrib *attrib) {
+  // Initialize the current ambient light total and newly enabled
+  // light list
+  Colorf cur_ambient_light(0.0f, 0.0f, 0.0f, 1.0f);
+  int i;
+  for (i = 0; i < _max_lights; i++) {
+    _light_info[i]._next_enabled = 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);
+    nassertv(light != (Light *)NULL);
+
+    num_enabled++;
+    enable_lighting(true);
+
+    if (light->get_type() == AmbientLight::get_class_type()) {
+      // Ambient lights don't require specific light ids; simply add
+      // in the ambient contribution to the current total
+      cur_ambient_light += light->get_color();
+        
+    } else {
+      // Check to see if this light has already been bound to an id
+      _cur_light_id = -1;
+      for (i = 0; i < _max_lights; i++) {
+        if (_light_info[i]._light == light) {
+          // Light has already been bound to an id, we only need to
+          // enable the light, not reapply it.
+          _cur_light_id = -2;
+          enable_light(i, true);
+          _light_info[i]._next_enabled = true;
+          break;
+        }
+      }
+        
+      // See if there are any unbound light ids
+      if (_cur_light_id == -1) {
+        for (i = 0; i < _max_lights; i++) {
+          if (_light_info[i]._light == (Light *)NULL) {
+            _light_info[i]._light = light;
+            _cur_light_id = i;
+            break;
+          }
+        }
+      }
+        
+      // If there were no unbound light ids, see if we can replace
+      // a currently unused but previously bound id
+      if (_cur_light_id == -1) {
+        for (i = 0; i < _max_lights; i++) {
+          if (!attrib->has_light(_light_info[i]._light)) {
+            _light_info[i]._light = light;
+            _cur_light_id = i;
+            break;
+          }
+        }
+      }
+        
+      if (_cur_light_id >= 0) {
+        enable_light(_cur_light_id, true);
+        _light_info[i]._next_enabled = true;
+        
+        // We need to do something different for each type of light
+        light->apply(this);
+      } else if (_cur_light_id == -1) {
+        glgsg_cat.error()
+          << "issue_light() - failed to bind light to id" << endl;
+      }
+    }
+  }
+
+  // Disable all unused lights
+  for (i = 0; i < _max_lights; i++) {
+    if (!_light_info[i]._next_enabled) {
+      enable_light(i, false);
+    }
+  }
+
+  // If no lights were enabled, disable lighting
+  if (num_enabled == 0) {
+    enable_lighting(false);
+  } else {
+    call_glLightModelAmbient(cur_ambient_light);
+  }
+  report_errors();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::issue_material
 //       Access: Public, Virtual
@@ -3501,6 +3563,10 @@ issue_material(const MaterialAttrib *attrib) {
   const Material *material = attrib->get_material();
   if (material != (const Material *)NULL) {
     apply_material(material);
+  } else {
+    // Apply a default material when materials are turned off.
+    Material empty;
+    apply_material(&empty);
   }
   report_errors();
 }
@@ -3711,9 +3777,9 @@ issue_depth_offset(const DepthOffsetAttrib *attrib) {
   int offset = attrib->get_offset();
 
   if (offset != 0) {
-    GLfloat newfactor = 1.0f;
-    GLfloat newunits = (GLfloat)offset;
-    glPolygonOffset(newfactor, newunits);
+    // The relationship between these two parameters is a little
+    // unclear and poorly explained in the GL man pages.
+    glPolygonOffset((GLfloat) -offset, (GLfloat) -offset);
     enable_polygon_offset(true);
 
   } else {
@@ -3723,6 +3789,43 @@ issue_depth_offset(const DepthOffsetAttrib *attrib) {
   report_errors();
 }
 
+static CPT(RenderState) 
+get_unlit_state() {
+  static CPT(RenderState) state = NULL;
+  if (state == (const RenderState *)NULL) {
+    state = RenderState::make(LightAttrib::make_all_off());
+    state->ref();
+  }
+  return state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::reset_frame
+//       Access: Public, Virtual
+//  Description: Called before each frame is rendered, to allow the
+//               GSG a chance to do any internal cleanup before
+//               beginning the frame.
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+reset_frame() {
+  // Undo any lighting we had enabled last frame, to force the lights
+  // to be reissued, in case their parameters or positions have
+  // changed between frames.
+  if (_lighting_enabled_this_frame) {
+    for (int i = 0; i < _max_lights; i++) {
+      enable_light(i, false);
+      _light_info[i]._light = (Light *)NULL;
+    }
+
+    // Also force the lighting state to unlit, so that issue_light()
+    // will be guaranteed to be called next frame even if we have the
+    // same set of light pointers we had this frame.
+    modify_state(get_unlit_state());
+
+    _lighting_enabled_this_frame = false;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::wants_normals
 //       Access: Public, Virtual
@@ -3888,6 +3991,19 @@ end_decal(GeomNode *base_geom) {
   report_errors();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::depth_offset_decals
+//       Access: Public, Virtual
+//  Description: Returns true if this GSG can implement decals using a
+//               DepthOffsetAttrib, or false if that is unreliable
+//               and the three-step rendering process should be used
+//               instead.
+////////////////////////////////////////////////////////////////////
+bool GLGraphicsStateGuardian::
+depth_offset_decals() {
+  return gl_depth_offset_decals;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::get_internal_coordinate_system
 //       Access: Public, Virtual

+ 6 - 2
panda/src/glgsg/glGraphicsStateGuardian.h

@@ -164,7 +164,7 @@ public:
   //  virtual void issue_color(const ColorAttrib *attrib);
   virtual void issue_tex_matrix(const TexMatrixAttrib *attrib);
   virtual void issue_texture(const TextureAttrib *attrib);
-  //  virtual void issue_light(const LightAttrib *attrib);
+  virtual void issue_light(const LightAttrib *attrib);
   virtual void issue_material(const MaterialAttrib *attrib);
   virtual void issue_render_mode(const RenderModeAttrib *attrib);
   virtual void issue_texture_apply(const TextureApplyAttrib *attrib);
@@ -180,12 +180,16 @@ public:
   //  virtual void issue_stencil(const StencilAttrib *attrib);
   //  virtual void issue_clip_plane(const ClipPlaneAttrib *attrib);
 
+  virtual void reset_frame();
+
   virtual bool wants_normals(void) const;
   virtual bool wants_texcoords(void) const;
 
   virtual void begin_decal(GeomNode *base_geom, AllTransitionsWrapper &attrib);
   virtual void end_decal(GeomNode *base_geom);
 
+  virtual bool depth_offset_decals();
+
   virtual CoordinateSystem get_internal_coordinate_system() const;
   virtual float compute_distance_to(const LPoint3f &point) const;
 
@@ -357,7 +361,7 @@ protected:
   class LightInfo {
   public:
     INLINE LightInfo();
-    PT_Light _light;
+    PT(Light) _light;
     bool _enabled;
     bool _next_enabled;
   };

+ 11 - 0
panda/src/gobj/lens.cxx

@@ -467,6 +467,17 @@ get_up_vector() const {
   return _up_vector;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Lens::get_nodal_point
+//       Access: Published
+//  Description: Returns the center point of the lens: the point from
+//               which the lens is viewing.
+////////////////////////////////////////////////////////////////////
+LPoint3f Lens::
+get_nodal_point() const {
+  return get_view_mat().get_row3(3);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Lens::set_iod_offset
 //       Access: Published

+ 1 - 0
panda/src/gobj/lens.h

@@ -99,6 +99,7 @@ PUBLISHED:
   void set_view_vector(const LVector3f &view_vector, const LVector3f &up_vector);
   const LVector3f &get_view_vector() const;
   const LVector3f &get_up_vector() const;
+  LPoint3f get_nodal_point() const;
   void set_iod_offset(float offset);
   float get_iod_offset() const;
 

+ 5 - 3
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -123,6 +123,8 @@ class LensNode;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA GraphicsStateGuardianBase : public TypedReferenceCount {
 public:
+  virtual void reset_frame()=0;
+
   // These functions will be queried by the GeomIssuer to determine if
   // it should issue normals, texcoords, and/or colors, based on the
   // GSG's current state.
@@ -154,11 +156,11 @@ public:
   // coordinate space differently.
   virtual float compute_distance_to(const LPoint3f &point) const=0;
 
-  // These are used to implement decals.  If polygon_offset_decals()
+  // These are used to implement decals.  If depth_offset_decals()
   // returns true, none of the remaining functions will be called,
-  // since polygon offsets can be used to implement decals fully (and
+  // since depth offsets can be used to implement decals fully (and
   // usually faster).
-  virtual bool polygon_offset_decals()=0;
+  virtual bool depth_offset_decals()=0;
   virtual CPT(RenderState) begin_decal_base_first()=0;
   virtual CPT(RenderState) begin_decal_nested()=0;
   virtual CPT(RenderState) begin_decal_base_second()=0;

+ 35 - 12
panda/src/light/Sources.pp

@@ -8,24 +8,47 @@
     linmath
     
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx    
-
   #define SOURCES \
-     ambientLight.h config_light.h directionalLight.I  \
-     directionalLight.h light.h \
-     lightTransition.I lightTransition.h pointLight.I  \
-     pointLight.h pt_Light.h spotlight.I spotlight.h  \
-     vector_PT_Light.h 
+    ambientLight.I ambientLight.h \
+    config_light.h \
+    directionalLight.I directionalLight.h \
+    light.I light.h \
+    lightAttrib.I lightAttrib.h \
+    lightLensNode.I lightLensNode.h \
+    lightNode.I lightNode.h \
+    lightTransition.I lightTransition.h \
+    pointLight.I pointLight.h \
+    pt_Light.h \
+    spotlight.I spotlight.h \
+    vector_PT_Light.h 
     
   #define INCLUDED_SOURCES \
-     ambientLight.cxx config_light.cxx directionalLight.cxx light.cxx  \
-     lightTransition.cxx pointLight.cxx  \
-     pt_Light.cxx spotlight.cxx vector_PT_Light.cxx 
+    ambientLight.cxx \
+    config_light.cxx \
+    directionalLight.cxx \
+    light.cxx \
+    lightAttrib.cxx \
+    lightLensNode.cxx \
+    lightNode.cxx \
+    lightTransition.cxx \
+    pointLight.cxx \
+    pt_Light.cxx \
+    spotlight.cxx \
+    vector_PT_Light.cxx 
 
   #define INSTALL_HEADERS \
-    ambientLight.h directionalLight.I directionalLight.h light.h \
+    ambientLight.I ambientLight.h \
+    directionalLight.I directionalLight.h \
+    light.I light.h \
+    lightAttrib.I lightAttrib.h \
+    lightLensNode.I lightLensNode.h \
+    lightNode.I lightNode.h \
     lightNameClass.h \
-    lightTransition.I lightTransition.h pointLight.I pointLight.h \
-    pt_Light.h spotlight.I spotlight.h vector_PT_Light.h
+    lightTransition.I lightTransition.h \
+    pointLight.I pointLight.h \
+    pt_Light.h \
+    spotlight.I spotlight.h \
+    vector_PT_Light.h
 
   #define IGATESCAN all
 

+ 17 - 0
panda/src/light/ambientLight.I

@@ -0,0 +1,17 @@
+// Filename: ambientLight.I
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////

+ 97 - 15
panda/src/light/ambientLight.cxx

@@ -15,35 +15,53 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
-#include "ambientLight.h"
 
-#include <indent.h>
+#include "ambientLight.h"
+#include "graphicsStateGuardian.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
 
-////////////////////////////////////////////////////////////////////
-// Static variables
-////////////////////////////////////////////////////////////////////
 TypeHandle AmbientLight::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
 //     Function: AmbientLight::Constructor
-//       Access:
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-AmbientLight::AmbientLight(const string& name) : NamedNode(name)
+AmbientLight::
+AmbientLight(const string &name) : 
+  LightNode(name) 
 {
-  set_color(Colorf(0.2, 0.2, 0.2, 1));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: AmbientLight::output
-//       Access: Public, Virtual
-//  Description:
+//     Function: AmbientLight::Copy Constructor
+//       Access: Protected
+//  Description: Do not call the copy constructor directly; instead,
+//               use make_copy() or copy_subgraph() to make a copy of
+//               a node.
 ////////////////////////////////////////////////////////////////////
-void AmbientLight::
-output(ostream &out) const {
-  NamedNode::output(out);
+AmbientLight::
+AmbientLight(const AmbientLight &copy) :
+  LightNode(copy)
+{
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: AmbientLight::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated PandaNode that is a shallow
+//               copy of this one.  It will be a different pointer,
+//               but its internal data may or may not be shared with
+//               that of the original PandaNode.  No children will be
+//               copied.
+////////////////////////////////////////////////////////////////////
+PandaNode *AmbientLight::
+make_copy() const {
+  return new AmbientLight(*this);
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: AmbientLight::write
@@ -53,6 +71,70 @@ output(ostream &out) const {
 void AmbientLight::
 write(ostream &out, int indent_level) const {
   indent(out, indent_level) << *this << ":\n";
-  indent(out, indent_level + 2) << "color " << _color << "\n";
+  indent(out, indent_level + 2)
+    << "color " << get_color() << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AmbientLight::apply
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void AmbientLight::
+apply(GraphicsStateGuardian *gsg) {
+  gsg->apply_light(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AmbientLight::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               AmbientLight.
+////////////////////////////////////////////////////////////////////
+void AmbientLight::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AmbientLight::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void AmbientLight::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  LightNode::write_datagram(manager, dg);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: AmbientLight::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type AmbientLight is encountered
+//               in the Bam file.  It should create the AmbientLight
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *AmbientLight::
+make_from_bam(const FactoryParams &params) {
+  AmbientLight *node = new AmbientLight("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AmbientLight::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 AmbientLight.
+////////////////////////////////////////////////////////////////////
+void AmbientLight::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  LightNode::fillin(scan, manager);
+}

+ 41 - 47
panda/src/light/ambientLight.h

@@ -15,67 +15,59 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
 #ifndef AMBIENTLIGHT_H
 #define AMBIENTLIGHT_H
-//
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
-#include <pandabase.h>
 
-#include <graphicsStateGuardian.h>
-#include <luse.h>
-#include "pmap.h"
-#include <namedNode.h>
-#include "light.h"
+#include "pandabase.h"
 
-////////////////////////////////////////////////////////////////////
-// Defines
-////////////////////////////////////////////////////////////////////
+#include "lightNode.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : AmbientLight
-// Description :
+// Description : A light source that seems to illuminate all points in
+//               space at once.  This kind of light need not actually
+//               be part of the scene graph, since it has no meaningful
+//               position.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA AmbientLight : public Light, public NamedNode
-{
-  PUBLISHED:
-
-    AmbientLight( const string& name = "" );
-    ~AmbientLight( void ) { }
+class EXPCL_PANDA AmbientLight : public LightNode {
+PUBLISHED:
+  AmbientLight(const string &name);
 
-  public:
+protected:
+  AmbientLight(const AmbientLight &copy);
 
-    virtual void output( ostream &out ) const;
-    virtual void write( ostream &out, int indent_level = 0 ) const;
+public:
+  virtual PandaNode *make_copy() const;
+  virtual void write(ostream &out, int indent_level) const;
+  
+public:
+  virtual void apply(GraphicsStateGuardian *gsg);
 
-    virtual void apply( GraphicsStateGuardian* gsg ) {
-      gsg->apply_light( this );
-    }
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
 
-  public:
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
 
-    static TypeHandle get_class_type( void ) {
-      return _type_handle;
-    }
-    static void init_type( void ) {
-      Light::init_type();
-      NamedNode::init_type();
-      register_type( _type_handle, "AmbientLight",
-                        Light::get_class_type(),
-                        NamedNode::get_class_type() );
-    }
-    virtual TypeHandle get_type( void ) const {
-      return get_class_type();
-    }
-    virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-    virtual TypeHandle get_light_type( void ) const {
-      return get_class_type();
-    }
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    LightNode::init_type();
+    register_type(_type_handle, "AmbientLight",
+                  LightNode::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;
+private:
+  static TypeHandle _type_handle;
 };
 
 INLINE ostream &operator << (ostream &out, const AmbientLight &light) {
@@ -83,4 +75,6 @@ INLINE ostream &operator << (ostream &out, const AmbientLight &light) {
   return out;
 }
 
+#include "ambientLight.I"
+
 #endif

+ 12 - 0
panda/src/light/config_light.cxx

@@ -20,6 +20,9 @@
 #include "ambientLight.h"
 #include "directionalLight.h"
 #include "light.h"
+#include "lightAttrib.h"
+#include "lightLensNode.h"
+#include "lightNode.h"
 #include "lightTransition.h"
 #include "pointLight.h"
 #include "spotlight.h"
@@ -33,7 +36,16 @@ ConfigureFn(config_light) {
   AmbientLight::init_type();
   DirectionalLight::init_type();
   Light::init_type();
+  LightAttrib::init_type();
+  LightLensNode::init_type();
+  LightNode::init_type();
   LightTransition::init_type();
   PointLight::init_type();
   Spotlight::init_type();
+
+  LightAttrib::register_with_read_factory();
+  AmbientLight::register_with_read_factory();
+  DirectionalLight::register_with_read_factory();
+  PointLight::register_with_read_factory();
+  Spotlight::register_with_read_factory();
 }

+ 92 - 6
panda/src/light/directionalLight.I

@@ -16,22 +16,108 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
 ////////////////////////////////////////////////////////////////////
-//     Function: DirectionalLight::get_specular
+//     Function: DirectionalLight::CData::Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE Colorf DirectionalLight::get_specular( void ) const
+INLINE DirectionalLight::CData::
+CData() :
+  _specular_color(1.0f, 1.0f, 1.0f, 1.0f),
+  _point(0.0f, 0.0f, 0.0f),
+  _direction(LVector3f::forward())
 {
-  return _specular;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DirectionalLight::set_specular
+//     Function: DirectionalLight::CData::Copy Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE void DirectionalLight::set_specular( const Colorf& color )
+INLINE DirectionalLight::CData::
+CData(const DirectionalLight::CData &copy) :
+  _specular_color(copy._specular_color),
+  _point(copy._point),
+  _direction(copy._direction)
 {
-  _specular = color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::get_specular_color
+//       Access: Public
+//  Description: Returns the color of specular highlights generated by
+//               the light.
+////////////////////////////////////////////////////////////////////
+INLINE const Colorf &DirectionalLight::
+get_specular_color() const {
+  CDReader cdata(_cycler);
+  return cdata->_specular_color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::set_specular_color
+//       Access: Public
+//  Description: Sets the color of specular highlights generated by
+//               the light.
+////////////////////////////////////////////////////////////////////
+INLINE void DirectionalLight::
+set_specular_color(const Colorf &color) {
+  CDWriter cdata(_cycler);
+  cdata->_specular_color = color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::get_point
+//       Access: Public
+//  Description: Returns the point in space at which the light is
+//               located.  This is local to the coordinate space in
+//               which the light is assigned.
+//
+//               This actually has no bearing on the visual effect of
+//               the light, since the light is rendered as if it were
+//               infinitely far away.  This is only used to create a
+//               visible representation of the light.
+////////////////////////////////////////////////////////////////////
+INLINE const LPoint3f &DirectionalLight::
+get_point() const {
+  CDReader cdata(_cycler);
+  return cdata->_point;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::set_point
+//       Access: Public
+//  Description: Sets the point in space at which the light is located.
+////////////////////////////////////////////////////////////////////
+INLINE void DirectionalLight::
+set_point(const LPoint3f &point) {
+  CDWriter cdata(_cycler);
+  cdata->_point = point;
+  mark_viz_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::get_direction
+//       Access: Public
+//  Description: Returns the direction in which the light is aimed.
+//               This is local to the coordinate space in which the
+//               light is assigned.
+////////////////////////////////////////////////////////////////////
+INLINE const LVector3f &DirectionalLight::
+get_direction() const {
+  CDReader cdata(_cycler);
+  return cdata->_direction;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::set_direction
+//       Access: Public
+//  Description: Sets the direction in which the light is aimed.
+////////////////////////////////////////////////////////////////////
+INLINE void DirectionalLight::
+set_direction(const LVector3f &direction) {
+  CDWriter cdata(_cycler);
+  cdata->_direction = direction;
+  mark_viz_stale();
 }

+ 155 - 14
panda/src/light/directionalLight.cxx

@@ -15,35 +15,106 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
-#include <pandabase.h>
+
 #include "directionalLight.h"
+#include "graphicsStateGuardian.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
 
-#include <indent.h>
+TypeHandle DirectionalLight::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-// Static variables
+//     Function: DirectionalLight::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
 ////////////////////////////////////////////////////////////////////
-TypeHandle DirectionalLight::_type_handle;
+CycleData *DirectionalLight::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void DirectionalLight::CData::
+write_datagram(BamWriter *, Datagram &dg) const {
+  _specular_color.write_datagram(dg);
+  _point.write_datagram(dg);
+  _direction.write_datagram(dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new Light.
+////////////////////////////////////////////////////////////////////
+void DirectionalLight::CData::
+fillin(DatagramIterator &scan, BamReader *) {
+  _specular_color.read_datagram(scan);
+  _point.read_datagram(scan);
+  _direction.read_datagram(scan);
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DirectionalLight::Constructor
-//       Access:
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-DirectionalLight::DirectionalLight(const string& name) : NamedNode(name)
+DirectionalLight::
+DirectionalLight(const string &name) : 
+  LightNode(name) 
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::Copy Constructor
+//       Access: Protected
+//  Description: Do not call the copy constructor directly; instead,
+//               use make_copy() or copy_subgraph() to make a copy of
+//               a node.
+////////////////////////////////////////////////////////////////////
+DirectionalLight::
+DirectionalLight(const DirectionalLight &copy) :
+  LightNode(copy),
+  _cycler(copy._cycler)
 {
-  set_color(Colorf(1, 1, 1, 1));
-  set_specular(Colorf(1, 1, 1, 1));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DirectionalLight::output
+//     Function: DirectionalLight::make_copy
 //       Access: Public, Virtual
-//  Description:
+//  Description: Returns a newly-allocated PandaNode that is a shallow
+//               copy of this one.  It will be a different pointer,
+//               but its internal data may or may not be shared with
+//               that of the original PandaNode.  No children will be
+//               copied.
+////////////////////////////////////////////////////////////////////
+PandaNode *DirectionalLight::
+make_copy() const {
+  return new DirectionalLight(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::xform
+//       Access: Public, Virtual
+//  Description: Transforms the contents of this PandaNode by the
+//               indicated matrix, if it means anything to do so.  For
+//               most kinds of PandaNodes, this does nothing.
 ////////////////////////////////////////////////////////////////////
 void DirectionalLight::
-output(ostream &out) const {
-  NamedNode::output(out);
+xform(const LMatrix4f &mat) {
+  LightNode::xform(mat);
+  CDWriter cdata(_cycler);
+  cdata->_point = cdata->_point * mat;
+  cdata->_direction = cdata->_direction * mat;
+  mark_viz_stale();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -54,6 +125,76 @@ output(ostream &out) const {
 void DirectionalLight::
 write(ostream &out, int indent_level) const {
   indent(out, indent_level) << *this << ":\n";
-  indent(out, indent_level + 2) << "color " << _color << "\n";
-  indent(out, indent_level + 2) << "specular " << _specular << "\n";
+  indent(out, indent_level + 2)
+    << "color " << get_color() << "\n";
+  indent(out, indent_level + 2)
+    << "specular color " << get_specular_color() << "\n";
+  indent(out, indent_level + 2)
+    << "direction " << get_direction() << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::apply
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DirectionalLight::
+apply(GraphicsStateGuardian *gsg) {
+  gsg->apply_light(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               DirectionalLight.
+////////////////////////////////////////////////////////////////////
+void DirectionalLight::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void DirectionalLight::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  LightNode::write_datagram(manager, dg);
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type DirectionalLight is encountered
+//               in the Bam file.  It should create the DirectionalLight
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *DirectionalLight::
+make_from_bam(const FactoryParams &params) {
+  DirectionalLight *node = new DirectionalLight("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DirectionalLight::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 DirectionalLight.
+////////////////////////////////////////////////////////////////////
+void DirectionalLight::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  LightNode::fillin(scan, manager);
+  manager->read_cdata(scan, _cycler);
 }

+ 72 - 59
panda/src/light/directionalLight.h

@@ -15,74 +15,87 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
 #ifndef DIRECTIONALLIGHT_H
 #define DIRECTIONALLIGHT_H
-//
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
-#include <pandabase.h>
 
-#include <graphicsStateGuardian.h>
-#include <luse.h>
-#include "pmap.h"
-#include <namedNode.h>
-#include "light.h"
+#include "pandabase.h"
 
-////////////////////////////////////////////////////////////////////
-// Defines
-////////////////////////////////////////////////////////////////////
+#include "lightNode.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : DirectionalLight
-// Description :
+// Description : A light shining from infinitely far away in a
+//               particular direction, like sunlight.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA DirectionalLight : public Light, public NamedNode
-{
-  PUBLISHED:
-
-    DirectionalLight( const string& name = "" );
-    ~DirectionalLight( void ) { }
-
-    INLINE Colorf get_specular( void ) const;
-    INLINE void set_specular( const Colorf& color );
-
-  public:
-
-    virtual void output( ostream &out ) const;
-    virtual void write( ostream &out, int indent_level = 0 ) const;
-
-    virtual void apply( GraphicsStateGuardian* gsg ) {
-      gsg->apply_light( this );
-    }
-
-  protected:
-
-    Colorf                                  _specular;
-
+class EXPCL_PANDA DirectionalLight : public LightNode {
+PUBLISHED:
+  DirectionalLight(const string &name);
+
+protected:
+  DirectionalLight(const DirectionalLight &copy);
+
+public:
+  virtual PandaNode *make_copy() const;
+  virtual void xform(const LMatrix4f &mat);
+  virtual void write(ostream &out, int indent_level) const;
+
+PUBLISHED:
+  INLINE const Colorf &get_specular_color() const;
+  INLINE void set_specular_color(const Colorf &color);
+  
+  INLINE const LPoint3f &get_point() const;
+  INLINE void set_point(const LPoint3f &point);
+  
+  INLINE const LVector3f &get_direction() const;
+  INLINE void set_direction(const LVector3f &direction);
+  
+public:
+  virtual void apply(GraphicsStateGuardian *gsg);
+
+private:
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
   public:
-
-    static TypeHandle get_class_type( void ) {
-      return _type_handle;
-    }
-    static void init_type( void ) {
-      Light::init_type();
-      NamedNode::init_type();
-      register_type( _type_handle, "DirectionalLight",
-                        Light::get_class_type(),
-                        NamedNode::get_class_type() );
-    }
-    virtual TypeHandle get_type( void ) const {
-      return get_class_type();
-    }
-    virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-    virtual TypeHandle get_light_type( void ) const {
-      return get_class_type();
-    }
-
-  private:
-
-    static TypeHandle                       _type_handle;
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+    Colorf _specular_color;
+    LPoint3f _point;
+    LVector3f _direction;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+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() {
+    LightNode::init_type();
+    register_type(_type_handle, "DirectionalLight",
+                  LightNode::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 DirectionalLight &light) {

+ 98 - 0
panda/src/light/light.I

@@ -0,0 +1,98 @@
+// Filename: light.I
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: Light::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Light::CData::
+CData() :
+  _color(1.0f, 1.0f, 1.0f, 1.0f),
+  _viz_geom_stale(true)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Light::CData::
+CData(const Light::CData &copy) :
+  _color(copy._color),
+  _viz_geom(copy._viz_geom),
+  _viz_geom_stale(copy._viz_geom_stale)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE Light::
+Light() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE Light::
+Light(const Light &copy) :
+  _cycler(copy._cycler)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::get_color
+//       Access: Published
+//  Description: Returns the basic color of the light.
+////////////////////////////////////////////////////////////////////
+INLINE const Colorf &Light::
+get_color() const {
+  CDReader cdata(_cycler);
+  return cdata->_color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::set_color
+//       Access: Published
+//  Description: Sets the basic color of the light.
+////////////////////////////////////////////////////////////////////
+INLINE void Light::
+set_color(const Colorf &color) {
+  CDWriter cdata(_cycler);
+  cdata->_color = color;
+  mark_viz_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::mark_viz_stale
+//       Access: Public
+//  Description: Indicates that the internal visualization object will
+//               need to be updated.
+////////////////////////////////////////////////////////////////////
+INLINE void Light::
+mark_viz_stale() {
+  CDWriter cdata(_cycler);
+  cdata->_viz_geom_stale = true;
+}

+ 100 - 2
panda/src/light/light.cxx

@@ -15,9 +15,107 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
 #include "light.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle Light::_type_handle;
+
 
 ////////////////////////////////////////////////////////////////////
-// Static variables
+//     Function: Light::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
 ////////////////////////////////////////////////////////////////////
-TypeHandle Light::_type_handle;
+CycleData *Light::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void Light::CData::
+write_datagram(BamWriter *, Datagram &dg) const {
+  _color.write_datagram(dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new Light.
+////////////////////////////////////////////////////////////////////
+void Light::CData::
+fillin(DatagramIterator &scan, BamReader *) {
+  _color.read_datagram(scan);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+Light::
+~Light() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::get_viz
+//       Access: Public
+//  Description: Returns a GeomNode that may be rendered to visualize
+//               the Light.  This is used during the cull traversal to
+//               render the Lights that have been made visible.
+////////////////////////////////////////////////////////////////////
+qpGeomNode *Light::
+get_viz() {
+  CDReader cdata(_cycler);
+  if (cdata->_viz_geom_stale) {
+    CDWriter cdata_w(_cycler, cdata);
+
+    cdata_w->_viz_geom = new qpGeomNode("viz");
+    fill_viz_geom(cdata_w->_viz_geom);
+    cdata_w->_viz_geom_stale = false;
+  }
+  return cdata->_viz_geom;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::fill_viz_geom
+//       Access: Protected, Virtual
+//  Description: Fills the indicated GeomNode up with Geoms suitable
+//               for rendering this light.
+////////////////////////////////////////////////////////////////////
+void Light::
+fill_viz_geom(qpGeomNode *) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::write_datagram
+//       Access: Protected
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void Light::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Light::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 Light.
+////////////////////////////////////////////////////////////////////
+void Light::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  manager->read_cdata(scan, _cycler);
+}

+ 74 - 39
panda/src/light/light.h

@@ -15,63 +15,96 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
 #ifndef LIGHT_H
 #define LIGHT_H
-//
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
-#include <pandabase.h>
 
-#include <typedObject.h>
-#include <graphicsStateGuardian.h>
-#include <referenceCount.h>
+#include "pandabase.h"
 
-////////////////////////////////////////////////////////////////////
-// Defines
-////////////////////////////////////////////////////////////////////
+#include "referenceCount.h"
+#include "luse.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+#include "qpgeomNode.h"
+
+class PandaNode;
+class GraphicsStateGuardian;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : Light
-// Description :
+// Description : The abstract interface to all kinds of lights.  The
+//               actual light objects also inherit from PandaNode, and
+//               can therefore be added to the scene graph at some
+//               arbitrary point to define the coordinate system of
+//               effect.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA Light : virtual public ReferenceCount
-{
-    PUBLISHED:
+class EXPCL_PANDA Light : virtual public ReferenceCount {
+  // We inherit from ReferenceCount instead of TypedReferenceCount so
+  // that LightNode does not inherit from TypedObject twice.  Note
+  // that we also inherit virtually from ReferenceCount for the same
+  // reason.
+PUBLISHED:
+  INLINE Light();
+  INLINE Light(const Light &copy);
+  virtual ~Light();
 
-        Light( void ) { _color.set(0.0, 0.0, 0.0, 1.0); }
-        virtual ~Light( void ) { }
+  virtual PandaNode *as_node()=0;
 
-        INLINE Colorf get_color(void) const { return _color; }
-        INLINE void set_color(const Colorf& color) { _color = color; }
+  INLINE const Colorf &get_color() const;
+  INLINE void set_color(const Colorf &color);
 
-        virtual void output(ostream &out) const=0;
-        virtual void write(ostream &out, int indent_level = 0) const=0;
+public:
+  virtual void output(ostream &out) const=0;
+  virtual void write(ostream &out, int indent_level) const=0;
+  virtual void apply(GraphicsStateGuardian *gsg)=0;
 
-    public:
-        virtual void apply( GraphicsStateGuardian* gsg ) = 0;
+  qpGeomNode *get_viz();
 
-    protected:
+protected:
+  virtual void fill_viz_geom(qpGeomNode *viz_geom);
+  INLINE void mark_viz_stale();
 
-        Colorf                  _color;
+private:
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
-    public:
+    Colorf _color;
 
-        static TypeHandle get_class_type( void ) {
-            return _type_handle;
-        }
-        static void init_type( void ) {
-            ReferenceCount::init_type();
-            register_type( _type_handle, "Light",
-                        ReferenceCount::get_class_type() );
-        }
-        virtual TypeHandle get_light_type( void ) const {
-            return get_class_type();
-        }
+    PT(qpGeomNode) _viz_geom;
+    bool _viz_geom_stale;
+  };
 
-    private:
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
 
-        static TypeHandle                       _type_handle;
+protected:
+  void write_datagram(BamWriter *manager, Datagram &dg);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ReferenceCount::init_type();
+    register_type(_type_handle, "Light",
+                  ReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  
+private:
+  static TypeHandle _type_handle;
 };
 
 INLINE ostream &operator << (ostream &out, const Light &light) {
@@ -79,4 +112,6 @@ INLINE ostream &operator << (ostream &out, const Light &light) {
   return out;
 }
 
+#include "light.I"
+
 #endif

+ 88 - 0
panda/src/light/lightAttrib.I

@@ -0,0 +1,88 @@
+// Filename: lightAttrib.I
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: LightAttrib::Constructor
+//       Access: Private
+//  Description: Use LightAttrib::make() to construct a new
+//               LightAttrib object.
+////////////////////////////////////////////////////////////////////
+INLINE LightAttrib::
+LightAttrib() {
+  _operation = O_set;
+}
+
+////////////////////////////////////////////////////////////////////
+//     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::get_num_lights
+//       Access: Published
+//  Description: Returns the number of lights listed in the attribute.
+////////////////////////////////////////////////////////////////////
+INLINE int LightAttrib::
+get_num_lights() const {
+  return _lights.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::get_light
+//       Access: Published
+//  Description: Returns the nth lights listed in the attribute.
+////////////////////////////////////////////////////////////////////
+INLINE Light *LightAttrib::
+get_light(int n) const {
+  nassertr(n >= 0 && n < (int)_lights.size(), (Light *)NULL);
+  return _lights[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::is_identity
+//       Access: Published
+//  Description: Returns true if this is an identity attrib: it does
+//               not change the set of lights in use.
+////////////////////////////////////////////////////////////////////
+INLINE bool LightAttrib::
+is_identity() const {
+  return _operation != O_set && _lights.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::is_all_off
+//       Access: Published
+//  Description: Returns true if this attrib turns off all lights and
+//               turns none on.
+////////////////////////////////////////////////////////////////////
+INLINE bool LightAttrib::
+is_all_off() const {
+  return _operation == O_set && _lights.empty();
+}

+ 489 - 0
panda/src/light/lightAttrib.cxx

@@ -0,0 +1,489 @@
+// Filename: lightAttrib.cxx
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "lightAttrib.h"
+#include "pandaNode.h"
+#include "graphicsStateGuardianBase.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+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).
+////////////////////////////////////////////////////////////////////
+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);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new LightAttrib object that turns on (or
+//               off, according to op) the indicate light(s).
+////////////////////////////////////////////////////////////////////
+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);
+
+  attrib->_lights.sort();
+  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).
+////////////////////////////////////////////////////////////////////
+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);
+
+  attrib->_lights.sort();
+  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).
+////////////////////////////////////////////////////////////////////
+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);
+
+  attrib->_lights.sort();
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::has_light
+//       Access: Published
+//  Description: Returns true if the indicated light is listed in the
+//               attrib, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool LightAttrib::
+has_light(Light *light) const {
+  return _lights.find(light) != _lights.end();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::issue
+//       Access: Public, Virtual
+//  Description: Calls the appropriate method on the indicated GSG
+//               to issue the graphics commands appropriate to the
+//               given attribute.  This is normally called
+//               (indirectly) only from
+//               GraphicsStateGuardian::set_state() or modify_state().
+////////////////////////////////////////////////////////////////////
+void LightAttrib::
+issue(GraphicsStateGuardianBase *gsg) const {
+  gsg->issue_light(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void LightAttrib::
+output(ostream &out) const {
+  out << get_type() << ":";
+  if (_operation == O_set && _lights.empty()) {
+    out << "all off";
+  } else {
+    switch (_operation) {
+    case O_set:
+      out << "set";
+      break;
+    case O_add:
+      out << "add";
+      break;
+    case O_remove:
+      out << "remove";
+      break;
+    }
+
+    Lights::const_iterator li;
+    for (li = _lights.begin(); li != _lights.end(); ++li) {
+      Light *light = (*li);
+      out << " " << light->get_type();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived LightAttrib
+//               types to return a unique number indicating whether
+//               this LightAttrib is equivalent to the other one.
+//
+//               This should return 0 if the two LightAttrib 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 LightAttrib
+//               objects whose get_type() functions return the same.
+////////////////////////////////////////////////////////////////////
+int LightAttrib::
+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;
+  }
+
+  Lights::const_iterator li = _lights.begin();
+  Lights::const_iterator oli = ta->_lights.begin();
+
+  while (li != _lights.end() && oli != ta->_lights.end()) {
+    Light *light = (*li);
+    Light *other_light = (*oli);
+
+    if (light != other_light) {
+      return light < other_light ? -1 : 1;
+    }
+
+    ++li;
+    ++oli;
+  }
+
+  if (li != _lights.end()) {
+    return 1;
+  }
+  if (oli != ta->_lights.end()) {
+    return -1;
+  }
+  
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::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.
+//
+//               This should return the result of applying the other
+//               RenderAttrib to a node in the scene graph below this
+//               RenderAttrib, which was already applied.  In most
+//               cases, the result is the same as the other
+//               RenderAttrib (that is, a subsequent RenderAttrib
+//               completely replaces the preceding one).  On the other
+//               hand, some kinds of RenderAttrib (for instance,
+//               ColorTransformAttrib) might combine in meaningful
+//               ways.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) LightAttrib::
+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.
+    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();
+
+  // 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.
+      *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;
+      ++ai;
+      ++bi;
+      ++result;
+    }
+  }
+
+  while (ai != _lights.end()) {
+    *result = *ai;
+    ++ai;
+    ++result;
+  }
+
+  while (bi != other->_lights.end()) {
+    *result = *bi;
+    ++bi;
+    ++result;
+  }
+
+  return return_new(new_attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     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.
+////////////////////////////////////////////////////////////////////
+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;
+  }
+
+  return return_new(new_attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               LightAttrib.
+////////////////////////////////////////////////////////////////////
+void LightAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+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);
+
+  Lights::const_iterator li;
+  for (li = _lights.begin(); li != _lights.end(); ++li) {
+    Light *light = (*li);
+    manager->write_pointer(dg, light->as_node());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::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 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) {
+    PandaNode *node;
+    DCAST_INTO_R(node, p_list[pi++], pi);
+    (*li) = node->as_light();
+  }
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type LightAttrib is encountered
+//               in the Bam file.  It should create the LightAttrib
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *LightAttrib::
+make_from_bam(const FactoryParams &params) {
+  LightAttrib *attrib = new LightAttrib;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::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 LightAttrib.
+////////////////////////////////////////////////////////////////////
+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++) {
+    manager->read_pointer(scan);
+    _lights.push_back(NULL);
+  }
+}

+ 116 - 0
panda/src/light/lightAttrib.h

@@ -0,0 +1,116 @@
+// Filename: lightAttrib.h
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 LIGHTATTRIB_H
+#define LIGHTATTRIB_H
+
+#include "pandabase.h"
+
+#include "light.h"
+#include "renderAttrib.h"
+#include "ordered_vector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : LightAttrib
+// Description : Indicates which set of lights should be considered
+//               "on" to illuminate geometry at this level and below.
+//               A LightAttrib can either add lights or remove lights
+//               from the total set of "on" lights.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA LightAttrib : public RenderAttrib {
+private:
+  INLINE LightAttrib();
+
+PUBLISHED:
+  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, 
+                                Light *light1, Light *light2);
+  static CPT(RenderAttrib) make(Operation op, 
+                                Light *light1, Light *light2,
+                                Light *light3);
+  static CPT(RenderAttrib) make(Operation op, 
+                                Light *light1, Light *light2,
+                                Light *light3, Light *light4);
+
+  INLINE Operation get_operation() const;
+
+  INLINE int get_num_lights() const;
+  INLINE Light *get_light(int n) const;
+  bool has_light(Light *light) const;
+
+  INLINE bool is_identity() const;
+  INLINE bool is_all_off() const;
+
+public:
+  virtual void issue(GraphicsStateGuardianBase *gsg) const;
+  virtual void output(ostream &out) const;
+
+protected:
+  virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
+  virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
+  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;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+
+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() {
+    RenderAttrib::init_type();
+    register_type(_type_handle, "LightAttrib",
+                  RenderAttrib::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;
+};
+
+#include "lightAttrib.I"
+
+#endif
+

+ 17 - 0
panda/src/light/lightLensNode.I

@@ -0,0 +1,17 @@
+// Filename: lightLensNode.I
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////

+ 115 - 0
panda/src/light/lightLensNode.cxx

@@ -0,0 +1,115 @@
+// Filename: lightLensNode.cxx
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "lightLensNode.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle LightLensNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightLensNode::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+LightLensNode::
+LightLensNode(const string &name) : 
+  qpLensNode(name) 
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightLensNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+LightLensNode::
+LightLensNode(const LightLensNode &copy) : 
+  Light(copy),
+  qpLensNode(copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightLensNode::as_node
+//       Access: Published, Virtual
+//  Description: Returns the Light object upcast to a PandaNode.
+////////////////////////////////////////////////////////////////////
+PandaNode *LightLensNode::
+as_node() {
+  return this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightLensNode::as_light
+//       Access: Public, Virtual
+//  Description: Cross-casts the node to a Light pointer, if it is one
+//               of the four kinds of Light nodes, or returns NULL if
+//               it is not.
+////////////////////////////////////////////////////////////////////
+Light *LightLensNode::
+as_light() {
+  return this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightLensNode::output
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void LightLensNode::
+output(ostream &out) const {
+  qpLensNode::output(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightLensNode::write
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void LightLensNode::
+write(ostream &out, int indent_level) const {
+  qpLensNode::write(out, indent_level);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightLensNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void LightLensNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  qpLensNode::write_datagram(manager, dg);
+  Light::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightLensNode::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 LightLensNode.
+////////////////////////////////////////////////////////////////////
+void LightLensNode::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  qpLensNode::fillin(scan, manager);
+  Light::fillin(scan, manager);
+}

+ 81 - 0
panda/src/light/lightLensNode.h

@@ -0,0 +1,81 @@
+// Filename: lightLensNode.h
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 LIGHTLENSNODE_H
+#define LIGHTLENSNODE_H
+
+#include "pandabase.h"
+
+#include "light.h"
+#include "qplensNode.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : LightLensNode
+// Description : A derivative of Light and of LensNode.  
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA LightLensNode : public Light, public qpLensNode {
+PUBLISHED:
+  LightLensNode(const string &name);
+
+protected:
+  LightLensNode(const LightLensNode &copy);
+
+public:
+  virtual PandaNode *as_node();
+  virtual Light *as_light();
+
+PUBLISHED:
+  // We have to explicitly publish these because they resolve the
+  // multiple inheritance.
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+public:
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    Light::init_type();
+    qpLensNode::init_type();
+    register_type(_type_handle, "LightLensNode",
+                  Light::get_class_type(),
+                  qpLensNode::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 LightLensNode &light) {
+  light.output(out);
+  return out;
+}
+
+#include "lightLensNode.I"
+
+#endif

+ 17 - 0
panda/src/light/lightNode.I

@@ -0,0 +1,17 @@
+// Filename: lightNode.I
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////

+ 115 - 0
panda/src/light/lightNode.cxx

@@ -0,0 +1,115 @@
+// Filename: lightNode.cxx
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "lightNode.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle LightNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightNode::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+LightNode::
+LightNode(const string &name) : 
+  PandaNode(name) 
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+LightNode::
+LightNode(const LightNode &copy) : 
+  Light(copy),
+  PandaNode(copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightNode::as_node
+//       Access: Published, Virtual
+//  Description: Returns the Light object upcast to a PandaNode.
+////////////////////////////////////////////////////////////////////
+PandaNode *LightNode::
+as_node() {
+  return this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightNode::as_light
+//       Access: Public, Virtual
+//  Description: Cross-casts the node to a Light pointer, if it is one
+//               of the four kinds of Light nodes, or returns NULL if
+//               it is not.
+////////////////////////////////////////////////////////////////////
+Light *LightNode::
+as_light() {
+  return this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightNode::output
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void LightNode::
+output(ostream &out) const {
+  PandaNode::output(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightNode::write
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void LightNode::
+write(ostream &out, int indent_level) const {
+  PandaNode::write(out, indent_level);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void LightNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PandaNode::write_datagram(manager, dg);
+  Light::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LightNode::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 LightNode.
+////////////////////////////////////////////////////////////////////
+void LightNode::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  PandaNode::fillin(scan, manager);
+  Light::fillin(scan, manager);
+}

+ 83 - 0
panda/src/light/lightNode.h

@@ -0,0 +1,83 @@
+// Filename: lightNode.h
+// Created by:  drose (26Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 LIGHTNODE_H
+#define LIGHTNODE_H
+
+#include "pandabase.h"
+
+#include "light.h"
+#include "pandaNode.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : LightNode
+// Description : A derivative of Light and of PandaNode.  All kinds of
+//               Light except Spotlight (which must inherit from
+//               LensNode instead) inherit from this class.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA LightNode : public Light, public PandaNode {
+PUBLISHED:
+  LightNode(const string &name);
+
+protected:
+  LightNode(const LightNode &copy);
+
+public:
+  virtual PandaNode *as_node();
+  virtual Light *as_light();
+
+PUBLISHED:
+  // We have to explicitly publish these because they resolve the
+  // multiple inheritance.
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+public:
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    Light::init_type();
+    PandaNode::init_type();
+    register_type(_type_handle, "LightNode",
+                  Light::get_class_type(),
+                  PandaNode::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 LightNode &light) {
+  light.output(out);
+  return out;
+}
+
+#include "lightNode.I"
+
+#endif

+ 3 - 1
panda/src/light/light_composite1.cxx

@@ -1,5 +1,7 @@
-
 #include "light.cxx"
+#include "lightAttrib.cxx"
+#include "lightLensNode.cxx"
+#include "lightNode.cxx"
 #include "lightTransition.cxx"
 #include "pt_Light.cxx"
 #include "vector_PT_Light.cxx"

+ 0 - 1
panda/src/light/light_composite2.cxx

@@ -1,4 +1,3 @@
-
 #include "config_light.cxx"
 #include "ambientLight.cxx"
 #include "spotlight.cxx"

+ 63 - 39
panda/src/light/pointLight.I

@@ -16,82 +16,106 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
 ////////////////////////////////////////////////////////////////////
-//     Function: PointLight::get_specular
+//     Function: PointLight::CData::Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE Colorf PointLight::get_specular( void ) const
+INLINE PointLight::CData::
+CData() :
+  _specular_color(1.0f, 1.0f, 1.0f, 1.0f),
+  _attenuation(1.0f, 0.0f, 0.0f),
+  _point(0.0f, 0.0f, 0.0f)
 {
-  return _specular;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PointLight::set_specular
+//     Function: PointLight::CData::Copy Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE void PointLight::set_specular( const Colorf& color )
+INLINE PointLight::CData::
+CData(const PointLight::CData &copy) :
+  _specular_color(copy._specular_color),
+  _attenuation(copy._attenuation),
+  _point(copy._point)
 {
-  _specular = color;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PointLight::get_constant_attenuation
+//     Function: PointLight::get_specular_color
 //       Access: Public
-//  Description:
+//  Description: Returns the color of specular highlights generated by
+//               the light.
 ////////////////////////////////////////////////////////////////////
-INLINE float PointLight::get_constant_attenuation( void ) const
-{
-  return _constant_attenuation;
+INLINE const Colorf &PointLight::
+get_specular_color() const {
+  CDReader cdata(_cycler);
+  return cdata->_specular_color;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PointLight::set_constant_attenuation
+//     Function: PointLight::set_specular_color
 //       Access: Public
-//  Description:
+//  Description: Sets the color of specular highlights generated by
+//               the light.
 ////////////////////////////////////////////////////////////////////
-INLINE void PointLight::set_constant_attenuation( float att )
-{
-  _constant_attenuation = att;
+INLINE void PointLight::
+set_specular_color(const Colorf &color) {
+  CDWriter cdata(_cycler);
+  cdata->_specular_color = color;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PointLight::get_linear_attenuation
+//     Function: PointLight::get_attenuation
 //       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE float PointLight::get_linear_attenuation( void ) const
-{
-  return _linear_attenuation;
+//  Description: Returns the terms of the attenuation equation for the
+//               light.  These are, in order, the constant, linear,
+//               and quadratic terms based on the distance from the
+//               point to the vertex.
+////////////////////////////////////////////////////////////////////
+INLINE const LVecBase3f &PointLight::
+get_attenuation() const {
+  CDReader cdata(_cycler);
+  return cdata->_attenuation;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PointLight::set_linear_attenuation
+//     Function: PointLight::set_attenuation
 //       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void PointLight::set_linear_attenuation( float att )
-{
-  _linear_attenuation = att;
+//  Description: Sets the terms of the attenuation equation for the
+//               light.  These are, in order, the constant, linear,
+//               and quadratic terms based on the distance from the
+//               point to the vertex.
+////////////////////////////////////////////////////////////////////
+INLINE void PointLight::
+set_attenuation(const LVecBase3f &attenuation) {
+  CDWriter cdata(_cycler);
+  cdata->_attenuation = attenuation;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PointLight::get_quadratic_attenuation
+//     Function: PointLight::get_point
 //       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE float PointLight::get_quadratic_attenuation( void ) const
-{
-  return _quadratic_attenuation;
+//  Description: Returns the point in space at which the light is
+//               located.  This is local to the coordinate space in
+//               which the light is assigned.
+////////////////////////////////////////////////////////////////////
+INLINE const LPoint3f &PointLight::
+get_point() const {
+  CDReader cdata(_cycler);
+  return cdata->_point;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PointLight::set_quadratic_attenuation
+//     Function: PointLight::set_point
 //       Access: Public
-//  Description:
+//  Description: Sets the point in space at which the light is located.
 ////////////////////////////////////////////////////////////////////
-INLINE void PointLight::set_quadratic_attenuation( float att )
-{
-  _quadratic_attenuation = att;
+INLINE void PointLight::
+set_point(const LPoint3f &point) {
+  CDWriter cdata(_cycler);
+  cdata->_point = point;
+  mark_viz_stale();
 }

+ 153 - 17
panda/src/light/pointLight.cxx

@@ -15,36 +15,105 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
 #include "pointLight.h"
+#include "graphicsStateGuardian.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle PointLight::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-// Static variables
+//     Function: PointLight::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
 ////////////////////////////////////////////////////////////////////
-TypeHandle PointLight::_type_handle;
+CycleData *PointLight::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointLight::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void PointLight::CData::
+write_datagram(BamWriter *, Datagram &dg) const {
+  _specular_color.write_datagram(dg);
+  _attenuation.write_datagram(dg);
+  _point.write_datagram(dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointLight::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new Light.
+////////////////////////////////////////////////////////////////////
+void PointLight::CData::
+fillin(DatagramIterator &scan, BamReader *) {
+  _specular_color.read_datagram(scan);
+  _attenuation.read_datagram(scan);
+  _point.read_datagram(scan);
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PointLight::Constructor
-//       Access:
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-PointLight::PointLight(const string& name) : NamedNode(name)
+PointLight::
+PointLight(const string &name) : 
+  LightNode(name) 
 {
-  set_color(Colorf(1, 1, 1, 1));
-  set_specular(Colorf(1, 1, 1, 1));
+}
 
-  set_constant_attenuation(1);
-  set_linear_attenuation(0);
-  set_quadratic_attenuation(0);
+////////////////////////////////////////////////////////////////////
+//     Function: PointLight::Copy Constructor
+//       Access: Protected
+//  Description: Do not call the copy constructor directly; instead,
+//               use make_copy() or copy_subgraph() to make a copy of
+//               a node.
+////////////////////////////////////////////////////////////////////
+PointLight::
+PointLight(const PointLight &copy) :
+  LightNode(copy),
+  _cycler(copy._cycler)
+{
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PointLight::output
+//     Function: PointLight::make_copy
 //       Access: Public, Virtual
-//  Description:
+//  Description: Returns a newly-allocated PandaNode that is a shallow
+//               copy of this one.  It will be a different pointer,
+//               but its internal data may or may not be shared with
+//               that of the original PandaNode.  No children will be
+//               copied.
+////////////////////////////////////////////////////////////////////
+PandaNode *PointLight::
+make_copy() const {
+  return new PointLight(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointLight::xform
+//       Access: Public, Virtual
+//  Description: Transforms the contents of this PandaNode by the
+//               indicated matrix, if it means anything to do so.  For
+//               most kinds of PandaNodes, this does nothing.
 ////////////////////////////////////////////////////////////////////
 void PointLight::
-output(ostream &out) const {
-  NamedNode::output(out);
+xform(const LMatrix4f &mat) {
+  LightNode::xform(mat);
+  CDWriter cdata(_cycler);
+  cdata->_point = cdata->_point * mat;
+  mark_viz_stale();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -55,9 +124,76 @@ output(ostream &out) const {
 void PointLight::
 write(ostream &out, int indent_level) const {
   indent(out, indent_level) << *this << ":\n";
-  indent(out, indent_level + 2) << "color " << _color << "\n";
-  indent(out, indent_level + 2) << "specular " << _specular << "\n";
   indent(out, indent_level + 2)
-    << "attenuation " << _constant_attenuation << ", "
-    << _linear_attenuation << ", " << _quadratic_attenuation << "\n";
+    << "color " << get_color() << "\n";
+  indent(out, indent_level + 2)
+    << "specular color " << get_specular_color() << "\n";
+  indent(out, indent_level + 2)
+    << "attenuation " << get_attenuation() << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointLight::apply
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void PointLight::
+apply(GraphicsStateGuardian *gsg) {
+  gsg->apply_light(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointLight::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               PointLight.
+////////////////////////////////////////////////////////////////////
+void PointLight::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointLight::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void PointLight::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  LightNode::write_datagram(manager, dg);
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointLight::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type PointLight is encountered
+//               in the Bam file.  It should create the PointLight
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *PointLight::
+make_from_bam(const FactoryParams &params) {
+  PointLight *node = new PointLight("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointLight::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 PointLight.
+////////////////////////////////////////////////////////////////////
+void PointLight::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  LightNode::fillin(scan, manager);
+  manager->read_cdata(scan, _cycler);
 }

+ 72 - 71
panda/src/light/pointLight.h

@@ -15,86 +15,87 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
 #ifndef POINTLIGHT_H
 #define POINTLIGHT_H
-//
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
-#include <pandabase.h>
 
-#include <graphicsStateGuardian.h>
-#include <luse.h>
-#include "pmap.h"
-#include <namedNode.h>
-#include "light.h"
+#include "pandabase.h"
 
-////////////////////////////////////////////////////////////////////
-// Defines
-////////////////////////////////////////////////////////////////////
+#include "lightNode.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PointLight
-// Description :
+// Description : A light originating from a single point in space, and
+//               shining in all directions.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA PointLight : public Light, public NamedNode
-{
-  PUBLISHED:
-
-    PointLight( const string& name = "" );
-    ~PointLight( void ) { }
-
-    INLINE Colorf get_specular( void ) const;
-    INLINE void set_specular( const Colorf& color );
-
-    INLINE float get_constant_attenuation( void ) const;
-    INLINE void set_constant_attenuation( float att );
-
-    INLINE float get_linear_attenuation( void ) const;
-    INLINE void set_linear_attenuation( float att );
-
-    INLINE float get_quadratic_attenuation( void ) const;
-    INLINE void set_quadratic_attenuation( float att );
-
+class EXPCL_PANDA PointLight : public LightNode {
+PUBLISHED:
+  PointLight(const string &name);
+
+protected:
+  PointLight(const PointLight &copy);
+
+public:
+  virtual PandaNode *make_copy() const;
+  virtual void xform(const LMatrix4f &mat);
+  virtual void write(ostream &out, int indent_level) const;
+
+PUBLISHED:
+  INLINE const Colorf &get_specular_color() const;
+  INLINE void set_specular_color(const Colorf &color);
+  
+  INLINE const LVecBase3f &get_attenuation() const;
+  INLINE void set_attenuation(const LVecBase3f &attenuation);
+  
+  INLINE const LPoint3f &get_point() const;
+  INLINE void set_point(const LPoint3f &point);
+  
+public:
+  virtual void apply(GraphicsStateGuardian *gsg);
+
+private:
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
   public:
-
-    virtual void output( ostream &out ) const;
-    virtual void write( ostream &out, int indent_level = 0 ) const;
-
-    virtual void apply( GraphicsStateGuardian* gsg ) {
-      gsg->apply_light( this );
-    }
-
-  protected:
-
-    Colorf                              _specular;
-    float                               _constant_attenuation;
-    float                               _linear_attenuation;
-    float                               _quadratic_attenuation;
-
-  public:
-
-    static TypeHandle get_class_type( void ) {
-      return _type_handle;
-    }
-    static void init_type( void ) {
-      Light::init_type();
-      NamedNode::init_type();
-      register_type( _type_handle, "PointLight",
-                        Light::get_class_type(),
-                        NamedNode::get_class_type() );
-    }
-    virtual TypeHandle get_type( void ) const {
-      return get_class_type();
-    }
-    virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-    virtual TypeHandle get_light_type( void ) const {
-      return get_class_type();
-    }
-
-  private:
-
-    static TypeHandle                   _type_handle;
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+    Colorf _specular_color;
+    LVecBase3f _attenuation;
+    LPoint3f _point;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+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() {
+    LightNode::init_type();
+    register_type(_type_handle, "PointLight",
+                  LightNode::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 PointLight &light) {

+ 68 - 59
panda/src/light/spotlight.I

@@ -16,102 +16,111 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::get_exponent
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE float Spotlight::get_exponent( void ) const
-{
-  return _exponent;
-}
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::set_exponent
+//     Function: Spotlight::CData::Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE void Spotlight::set_exponent( float exponent )
+INLINE Spotlight::CData::
+CData() :
+  _exponent(0.0f),
+  _specular_color(1.0f, 1.0f, 1.0f, 1.0f),
+  _attenuation(1.0f, 0.0f, 0.0f)
 {
-  _exponent = exponent;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::get_specular
+//     Function: Spotlight::CData::Copy Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE Colorf Spotlight::get_specular( void ) const
+INLINE Spotlight::CData::
+CData(const Spotlight::CData &copy) :
+  _exponent(copy._exponent),
+  _specular_color(copy._specular_color),
+  _attenuation(copy._attenuation)
 {
-  return _specular;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::set_specular
+//     Function: Spotlight::get_exponent
 //       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void Spotlight::set_specular( const Colorf& color )
-{
-  _specular = color;
+//  Description: Returns the exponent that controls the amount of
+//               light falloff from the center of the spotlight.  See
+//               set_exponent().
+////////////////////////////////////////////////////////////////////
+INLINE float Spotlight::
+get_exponent() const {
+  CDReader cdata(_cycler);
+  return cdata->_exponent;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::get_constant_attenuation
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE float Spotlight::get_constant_attenuation( void ) const
-{
-  return _constant_attenuation;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::set_constant_attenuation
+//     Function: Spotlight::set_exponent
 //       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void Spotlight::set_constant_attenuation( float att )
-{
-  _constant_attenuation = att;
+//  Description: Sets the exponent that controls the amount of light
+//               falloff from the center of the spotlight.  The light
+//               is attenuated by the cosine of the angle between the
+//               direction of the light and the direction of the point
+//               being lighted, raised to the power of this exponent.
+//               Thus, higher exponents result in a more focused light
+//               source, regardless of the field-of-view of the lens.
+////////////////////////////////////////////////////////////////////
+INLINE void Spotlight::
+set_exponent(float exponent) {
+  CDWriter cdata(_cycler);
+  cdata->_exponent = exponent;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::get_linear_attenuation
+//     Function: Spotlight::get_specular_color
 //       Access: Public
-//  Description:
+//  Description: Returns the color of specular highlights generated by
+//               the light.
 ////////////////////////////////////////////////////////////////////
-INLINE float Spotlight::get_linear_attenuation( void ) const
-{
-  return _linear_attenuation;
+INLINE const Colorf &Spotlight::
+get_specular_color() const {
+  CDReader cdata(_cycler);
+  return cdata->_specular_color;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::set_linear_attenuation
+//     Function: Spotlight::set_specular_color
 //       Access: Public
-//  Description:
+//  Description: Sets the color of specular highlights generated by
+//               the light.
 ////////////////////////////////////////////////////////////////////
-INLINE void Spotlight::set_linear_attenuation( float att )
-{
-  _linear_attenuation = att;
+INLINE void Spotlight::
+set_specular_color(const Colorf &color) {
+  CDWriter cdata(_cycler);
+  cdata->_specular_color = color;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::get_quadratic_attenuation
+//     Function: Spotlight::get_attenuation
 //       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE float Spotlight::get_quadratic_attenuation( void ) const
-{
-  return _quadratic_attenuation;
+//  Description: Returns the terms of the attenuation equation for the
+//               light.  These are, in order, the constant, linear,
+//               and quadratic terms based on the distance from the
+//               point to the vertex.
+////////////////////////////////////////////////////////////////////
+INLINE const LVecBase3f &Spotlight::
+get_attenuation() const {
+  CDReader cdata(_cycler);
+  return cdata->_attenuation;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::set_quadratic_attenuation
+//     Function: Spotlight::set_attenuation
 //       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void Spotlight::set_quadratic_attenuation( float att )
-{
-  _quadratic_attenuation = att;
+//  Description: Sets the terms of the attenuation equation for the
+//               light.  These are, in order, the constant, linear,
+//               and quadratic terms based on the distance from the
+//               point to the vertex.
+////////////////////////////////////////////////////////////////////
+INLINE void Spotlight::
+set_attenuation(const LVecBase3f &attenuation) {
+  CDWriter cdata(_cycler);
+  cdata->_attenuation = attenuation;
 }

+ 223 - 154
panda/src/light/spotlight.cxx

@@ -15,50 +15,124 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
 #include "spotlight.h"
-#include "lightTransition.h"
-#include "config_light.h"
+#include "graphicsStateGuardian.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "colorAttrib.h"
 
-#include <renderRelation.h>
-#include <perspectiveLens.h>
-#include <texture.h>
-#include <geomNode.h>
-#include <geomTrifan.h>
-#include <depthWriteTransition.h>
-#include <transparencyTransition.h>
-#include <cullFaceTransition.h>
-#include <textureTransition.h>
+TypeHandle Spotlight::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-// Static variables
+//     Function: Spotlight::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
 ////////////////////////////////////////////////////////////////////
-TypeHandle Spotlight::_type_handle;
+CycleData *Spotlight::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void Spotlight::CData::
+write_datagram(BamWriter *, Datagram &dg) const {
+  dg.add_float32(_exponent);
+  _specular_color.write_datagram(dg);
+  _attenuation.write_datagram(dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new Light.
+////////////////////////////////////////////////////////////////////
+void Spotlight::CData::
+fillin(DatagramIterator &scan, BamReader *) {
+  _exponent = scan.get_float32();
+  _specular_color.read_datagram(scan);
+  _attenuation.read_datagram(scan);
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Spotlight::Constructor
-//       Access:
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-Spotlight::Spotlight(const string& name) : LensNode(name)
+Spotlight::
+Spotlight(const string &name) : 
+  LightLensNode(name) 
 {
-  set_exponent(1);
+  cerr << "Creating " << *this << ", this = " << (int)this
+       << " node this = " << (int)(PandaNode *)this 
+       << " light this = " << (int)(Light *)this 
+       << " bounded this = " << (int)(BoundedObject *)this << "\n";
+  _internal_bound.get_bound();
+  cerr << "got bound 1\n";
+  get_internal_bound();
+  cerr << "got bound 2\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::foo
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+void Spotlight::
+foo() {
+  cerr << "checking bound\n";
+  get_internal_bound();
+  cerr << "done\n";
+}
 
-  set_color(Colorf(1, 1, 1, 1));
-  set_specular(Colorf(1, 1, 1, 1));
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::Copy Constructor
+//       Access: Protected
+//  Description: Do not call the copy constructor directly; instead,
+//               use make_copy() or copy_subgraph() to make a copy of
+//               a node.
+////////////////////////////////////////////////////////////////////
+Spotlight::
+Spotlight(const Spotlight &copy) :
+  LightLensNode(copy),
+  _cycler(copy._cycler)
+{
+}
 
-  set_constant_attenuation(1);
-  set_linear_attenuation(0);
-  set_quadratic_attenuation(0);
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated PandaNode that is a shallow
+//               copy of this one.  It will be a different pointer,
+//               but its internal data may or may not be shared with
+//               that of the original PandaNode.  No children will be
+//               copied.
+////////////////////////////////////////////////////////////////////
+PandaNode *Spotlight::
+make_copy() const {
+  return new Spotlight(*this);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::output
+//     Function: Spotlight::xform
 //       Access: Public, Virtual
-//  Description:
+//  Description: Transforms the contents of this PandaNode by the
+//               indicated matrix, if it means anything to do so.  For
+//               most kinds of PandaNodes, this does nothing.
 ////////////////////////////////////////////////////////////////////
 void Spotlight::
-output(ostream &out) const {
-  NamedNode::output(out);
+xform(const LMatrix4f &mat) {
+  LightLensNode::xform(mat); 
+  mark_viz_stale();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -69,47 +143,53 @@ output(ostream &out) const {
 void Spotlight::
 write(ostream &out, int indent_level) const {
   indent(out, indent_level) << *this << ":\n";
-  indent(out, indent_level + 2) << "color " << _color << "\n";
-  indent(out, indent_level + 2) << "specular " << _specular << "\n";
   indent(out, indent_level + 2)
-    << "attenuation " << _constant_attenuation << ", "
-    << _linear_attenuation << ", " << _quadratic_attenuation << "\n";
-  indent(out, indent_level + 2) << "exponent " << _exponent << "\n";
+    << "color " << get_color() << "\n";
+  indent(out, indent_level + 2)
+    << "specular color " << get_specular_color() << "\n";
+  indent(out, indent_level + 2)
+    << "attenuation " << get_attenuation() << "\n";
+  indent(out, indent_level + 2)
+    << "exponent " << get_exponent() << "\n";
+
+  Lens *lens = get_lens();
+  if (lens != (Lens *)NULL) {
+    lens->write(out, indent_level + 2);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::get_cutoff_angle
-//       Access:
+//     Function: Spotlight::apply
+//       Access: Public, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
-float Spotlight::get_cutoff_angle(void) const
-{
-  Lens* proj = ((LensNode *)this)->get_lens();
-  float cutoff = 0;
-  if (proj->get_type() == PerspectiveLens::get_class_type()) {
-    const LVecBase2f &fov = ((PerspectiveLens *)proj)->get_fov();
-    cutoff = fov[0];
-  }
-  else
-    light_cat.error()
-      << "Spotlight::get_cutoff_angle() - spotlight has a non "
-      << "perspective lens!" << endl;
-  return cutoff;
+void Spotlight::
+apply(GraphicsStateGuardian *gsg) {
+  gsg->apply_light(this);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Spotlight::make_image
-//       Access:
-//  Description:
+//       Access: Public
+//  Description: Generates an image into the indicated texture of a
+//               circle with a soft edge that corresponds to the
+//               falloff of the spotlight.  This is intended to be
+//               used to implement projected texture spotlights; the
+//               image can be applied to geometry with UV's computed
+//               appropriate to simulate the texture's region of
+//               influence.
+//
+//               Returns true if the image is successfully generated,
+//               false otherwise.
 ////////////////////////////////////////////////////////////////////
-bool Spotlight::make_image(Texture* texture, float radius)
-{
+bool Spotlight::
+make_image(Texture *texture, float radius) {
   if (texture == NULL) {
     light_cat.error()
       << "Spotlight::make_image() - NULL texture" << endl;
     return false;
   }
-  PixelBuffer* pb = texture->_pbuffer;
+  PixelBuffer *pb = texture->_pbuffer;
   int size = pb->get_xsize();
   if (size == 0) {
     light_cat.error()
@@ -117,14 +197,13 @@ bool Spotlight::make_image(Texture* texture, float radius)
     return false;
   }
 
-  uchar color[3];
-  color[0] = (int)(_color[0] * 255.0f);
-  color[1] = (int)(_color[1] * 255.0f);
-  color[2] = (int)(_color[2] * 255.0f);
+  const Colorf &color = get_color();
+
+  RGBColorf scaled_color;
+  scaled_color[0] = (color[0] * 255.0f);
+  scaled_color[1] = (color[1] * 255.0f);
+  scaled_color[2] = (color[2] * 255.0f);
 
-  //  float cutoff = get_cutoff_angle();
-  //  float dist = 1 / (float)tan(cutoff);
-  //  int bufsize = size * size * 3;
   int half_width = (size - 2) / 2;
   float dXY = 1 / (float)half_width;
   float Y = dXY + dXY;
@@ -144,13 +223,13 @@ bool Spotlight::make_image(Texture* texture, float radius)
         intensity = 1.0f;
           else if (D < 1.0f)
         intensity = pow(cos((D-radius) /
-                (1.0f-radius) * (MathNumbers::pi_f*0.5f)), _exponent);
+                (1.0f-radius) * (MathNumbers::pi_f*0.5f)), get_exponent());
       else
         intensity = 0;
 
-      C[0] = (uchar)(intensity * color[0]);
-      C[1] = (uchar)(intensity * color[1]);
-      C[2] = (uchar)(intensity * color[2]);
+      C[0] = (uchar)(intensity * scaled_color[0]);
+      C[1] = (uchar)(intensity * scaled_color[1]);
+      C[2] = (uchar)(intensity * scaled_color[2]);
 
       tx = x + half_width;
 
@@ -173,100 +252,90 @@ bool Spotlight::make_image(Texture* texture, float radius)
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Spotlight::make_geometry
-//       Access:
-//  Description:
+//     Function: Spotlight::fill_viz_geom
+//       Access: Protected, Virtual
+//  Description: Fills the indicated GeomNode up with Geoms suitable
+//               for rendering this light.
 ////////////////////////////////////////////////////////////////////
-NamedNode* Spotlight::
-make_geometry(float intensity, float length, int num_facets)
-{
-  Colorf diffuse = _color;
-  diffuse[3] = intensity;
-  Colorf black(0.0f, 0.0f, 0.0f, intensity);
-  float radius = length * (float)tan(deg_2_rad(get_cutoff_angle()));
-  float ang_inc = 2.0f*MathNumbers::pi_f / (float)num_facets;
-  int num_verts = num_facets + 1;
-  int num_indices = num_facets + 2;
-  LVector3f offset(0.0f, length, 0.0f);
-  LPoint3f first_last_vert(radius, length, 0.0f);
-
-  PTA_Vertexf coords=PTA_Vertexf::empty_array(num_verts);
-  PTA_ushort vindex=PTA_ushort::empty_array(num_indices);
-  PTA_Colorf colors=PTA_Colorf::empty_array(2);
-  PTA_ushort cindex=PTA_ushort::empty_array(num_indices);
-  PTA_int lengths=PTA_int::empty_array(1);
-
-  lengths[0] = num_indices;
-
-  float ang = ang_inc;
-  LPoint3f origin(0.0f, 0.0f, 0.0f);
-  LVector3f x_axis(1.0f, 0.0f, 0.0f);
-  LVector3f z_axis(0.0f, 0.0f, 1.0f);
-  LPoint3f dx, dz;
-  float t;
-
-  coords[0] = origin;
-  coords[1] = first_last_vert;
-  vindex[0] = 0;
-  vindex[1] = 1;
-  vindex[num_indices-1] = 1;
-
-  int i;
-  for (i = 2; i < num_indices-1; i++) {
-    float sine,cosine;
-
-    csincos(ang,&sine,&cosine);
-    t = cosine * radius;
-    dx = x_axis * t;
-    t = sine * radius;
-    dz = z_axis * t;
-    coords[i] = dx + dz + offset;
-    ang += ang_inc;
-    vindex[i] = i;
+void Spotlight::
+fill_viz_geom(qpGeomNode *viz_geom) {
+  Lens *lens = get_lens();
+  if (lens == (Lens *)NULL) {
+    return;
   }
+  
+  PT(Geom) geom = lens->make_geometry();
+  if (geom == (Geom *)NULL) {
+    return;
+  }
+
+  viz_geom->add_geom(geom, get_viz_state());
+}
 
-  colors[0] = diffuse;
-  colors[1] = black;
-  cindex[0] = 0;
-  for (i = 1; i < num_indices; i++)
-    cindex[i] = 1;
-
-  GeomTrifan* tfan = new GeomTrifan;
-  tfan->set_coords(coords, vindex);
-  tfan->set_colors(colors, G_PER_VERTEX, cindex);
-  tfan->set_num_prims(1);
-  tfan->set_lengths(lengths);
-
-  GeomNode* geomnode = new GeomNode("spotlight_frustum_geom");
-  geomnode->add_geom(tfan);
-
-  NamedNode* root = new NamedNode("spotlight_frustum");
-  RenderRelation* root_arc = new RenderRelation(root, geomnode);
-
-  // Disable lighting
-  LightTransition *light_trans =
-    new LightTransition(LightTransition::all_off());
-  root_arc->set_transition(light_trans);
-
-  // Disable texturing
-  TextureTransition *tex_trans =
-    new TextureTransition(TextureTransition::off());
-  root_arc->set_transition(tex_trans);
-
-  // Turn off writes to Z
-  DepthWriteTransition *depth_trans =
-    new DepthWriteTransition(DepthWriteTransition::off());
-  root_arc->set_transition(depth_trans);
-
-  // Enable transparency
-  TransparencyTransition *col_trans =
-    new TransparencyTransition(TransparencyProperty::M_alpha);
-  root_arc->set_transition(col_trans);
-
-  // Disable culling
-  CullFaceTransition *cull_trans =
-    new CullFaceTransition(CullFaceProperty::M_cull_none);
-  root_arc->set_transition(cull_trans);
-
-  return root;
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::get_viz_state
+//       Access: Private
+//  Description: Returns a RenderState for rendering the spotlight
+//               visualization.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) Spotlight::
+get_viz_state() {
+  return RenderState::make
+    (ColorAttrib::make_flat(get_color()));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               Spotlight.
+////////////////////////////////////////////////////////////////////
+void Spotlight::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void Spotlight::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  LightLensNode::write_datagram(manager, dg);
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type Spotlight is encountered
+//               in the Bam file.  It should create the Spotlight
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *Spotlight::
+make_from_bam(const FactoryParams &params) {
+  Spotlight *node = new Spotlight("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Spotlight::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 Spotlight.
+////////////////////////////////////////////////////////////////////
+void Spotlight::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  LightLensNode::fillin(scan, manager);
+  manager->read_cdata(scan, _cycler);
 }

+ 90 - 83
panda/src/light/spotlight.h

@@ -15,98 +15,105 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
 #ifndef SPOTLIGHT_H
 #define SPOTLIGHT_H
-//
-////////////////////////////////////////////////////////////////////
-// Includes
-////////////////////////////////////////////////////////////////////
-#include <pandabase.h>
 
-#include <graphicsStateGuardian.h>
-#include <luse.h>
-#include "pmap.h"
-#include <namedNode.h>
-#include <frustum.h>
-#include "light.h"
-#include <lensNode.h>
+#include "pandabase.h"
 
-////////////////////////////////////////////////////////////////////
-// Defines
-////////////////////////////////////////////////////////////////////
+#include "lightLensNode.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : Spotlight
-// Description :
+// Description : A light originating from a single point in space, and
+//               shining in a particular direction, with a cone-shaped
+//               falloff.
+//
+//               The Spotlight frustum is defined using a Lens, so it
+//               can have any of the properties that a camera lens can
+//               have.
+//
+//               Note that the class is named Spotlight instead of
+//               SpotLight, because "spotlight" is a single English
+//               word, instead of two words.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA Spotlight : public Light, public LensNode
-{
-  PUBLISHED:
-
-    Spotlight( const string& name = "" );
-    ~Spotlight( void ) { }
-
-    INLINE float get_exponent( void ) const;
-    INLINE void set_exponent( float exponent );
-
-    float get_cutoff_angle( void ) const;
-
-    INLINE Colorf get_specular( void ) const;
-    INLINE void set_specular( const Colorf& color );
-
-    INLINE float get_constant_attenuation( void ) const;
-    INLINE void set_constant_attenuation( float att );
-
-    INLINE float get_linear_attenuation( void ) const;
-    INLINE void set_linear_attenuation( float att );
-
-    INLINE float get_quadratic_attenuation( void ) const;
-    INLINE void set_quadratic_attenuation( float att );
-
-  public:
-
-    virtual void output( ostream &out ) const;
-    virtual void write( ostream &out, int indent_level = 0 ) const;
-
-    virtual void apply( GraphicsStateGuardian* gsg ) {
-      gsg->apply_light( this );
-    }
-
-    bool make_image(Texture* texture, float radius = 0.7);
-    NamedNode* make_geometry(float intensity = 0.05, float length = 20.0,
-        int num_facets = 36);
-
-  protected:
-
-    float                               _exponent;
-    Colorf                              _specular;
-    float                               _constant_attenuation;
-    float                               _linear_attenuation;
-    float                               _quadratic_attenuation;
-
+class EXPCL_PANDA Spotlight : public LightLensNode {
+PUBLISHED:
+  Spotlight(const string &name);
+  void foo();
+
+protected:
+  Spotlight(const Spotlight &copy);
+
+public:
+  virtual PandaNode *make_copy() const;
+  virtual void xform(const LMatrix4f &mat);
+  virtual void write(ostream &out, int indent_level) const;
+
+PUBLISHED:
+  INLINE float get_exponent() const;
+  INLINE void set_exponent(float exponent);
+  
+  INLINE const Colorf &get_specular_color() const;
+  INLINE void set_specular_color(const Colorf &color);
+  
+  INLINE const LVecBase3f &get_attenuation() const;
+  INLINE void set_attenuation(const LVecBase3f &attenuation);
+  
+public:
+  virtual void apply(GraphicsStateGuardian *gsg);
+
+  bool make_image(Texture *texture, float radius);
+
+protected:
+  virtual void fill_viz_geom(qpGeomNode *viz_geom);
+
+private:
+  CPT(RenderState) get_viz_state();
+
+private:
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
   public:
-
-    static TypeHandle get_class_type( void ) {
-      return _type_handle;
-    }
-    static void init_type( void ) {
-      Light::init_type();
-      LensNode::init_type();
-      register_type( _type_handle, "Spotlight",
-                           Light::get_class_type(),
-                           LensNode::get_class_type() );
-    }
-    virtual TypeHandle get_type( void ) const {
-      return get_class_type();
-    }
-    virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-    virtual TypeHandle get_light_type( void ) const {
-      return get_class_type();
-    }
-
-  private:
-
-    static TypeHandle                   _type_handle;
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+    float _exponent;
+    Colorf _specular_color;
+    LVecBase3f _attenuation;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+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() {
+    LightLensNode::init_type();
+    register_type(_type_handle, "Spotlight",
+                  LightLensNode::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 Spotlight &light) {

+ 3 - 0
panda/src/pgraph/Sources.pp

@@ -52,6 +52,7 @@
     renderModeAttrib.I renderModeAttrib.h \
     renderState.I renderState.h \
     qpsceneGraphReducer.I qpsceneGraphReducer.h \
+    sceneSetup.I sceneSetup.h \
     selectiveChildNode.I selectiveChildNode.h \
     qpsequenceNode.I qpsequenceNode.h \
     showBoundsEffect.I showBoundsEffect.h \
@@ -111,6 +112,7 @@
     renderModeAttrib.cxx \
     renderState.cxx \
     qpsceneGraphReducer.cxx \
+    sceneSetup.cxx \
     selectiveChildNode.cxx \
     qpsequenceNode.cxx \
     showBoundsEffect.cxx \
@@ -172,6 +174,7 @@
     renderModeAttrib.I renderModeAttrib.h \
     renderState.I renderState.h \
     qpsceneGraphReducer.I qpsceneGraphReducer.h \
+    sceneSetup.I sceneSetup.h \
     selectiveChildNode.I selectiveChildNode.h \
     qpsequenceNode.I qpsequenceNode.h \
     showBoundsEffect.I showBoundsEffect.h \

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

@@ -29,11 +29,9 @@
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 CullHandler::
-~CullHandler()
-{
+~CullHandler() {
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CullHandler::record_object
 //       Access: Public, Virtual

+ 1 - 0
panda/src/pgraph/cullTraverserData.cxx

@@ -157,6 +157,7 @@ get_fake_view_frustum_cull_state() {
        TextureAttrib::make_off(),
        RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
        1000);
+    state->ref();  // once more to guard against static destruction
   }
   return state;
 }

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

@@ -1143,6 +1143,18 @@ is_geom_node() const {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::as_light
+//       Access: Public, Virtual
+//  Description: Cross-casts the node to a Light pointer, if it is one
+//               of the four kinds of Light nodes, or returns NULL if
+//               it is not.
+////////////////////////////////////////////////////////////////////
+Light *PandaNode::
+as_light() {
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::propagate_stale_bound
 //       Access: Protected, Virtual
@@ -1244,7 +1256,8 @@ recompute_bound() {
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *PandaNode::
 recompute_internal_bound() {
-  return _internal_bound.recompute_bound();
+  BoundingVolume *result = _internal_bound.recompute_bound();
+  return result;
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -43,6 +43,7 @@
 class qpNodePathComponent;
 class qpCullTraverser;
 class CullTraverserData;
+class Light;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PandaNode
@@ -163,6 +164,7 @@ PUBLISHED:
 
 public:
   virtual bool is_geom_node() const;
+  virtual Light *as_light();
 
 protected:
   // Inherited from BoundedObject

+ 78 - 56
panda/src/pgraph/qpcullTraverser.I

@@ -17,6 +17,59 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::set_scene
+//       Access: Public
+//  Description: Sets the SceneSetup object that indicates the initial
+//               camera position, etc.  This must be called before
+//               traversal begins.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCullTraverser::
+set_scene(SceneSetup *scene_setup) {
+  _scene_setup = scene_setup;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::get_scene
+//       Access: Public
+//  Description: Returns the SceneSetup object.
+////////////////////////////////////////////////////////////////////
+INLINE SceneSetup *qpCullTraverser::
+get_scene() const {
+  return _scene_setup;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::get_camera_transform
+//       Access: Public
+//  Description: Returns the position of the camera relative to the
+//               starting node, without any compensating
+//               coordinate-system transforms that might have been
+//               introduced for the purposes of rendering.
+////////////////////////////////////////////////////////////////////
+INLINE const TransformState *qpCullTraverser::
+get_camera_transform() const {
+  return _scene_setup->get_camera_transform();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::get_render_transform
+//       Access: Public
+//  Description: Returns the position of the starting node relative
+//               to the camera, pretransformed as appropriate for
+//               rendering.
+//
+//               Note that this value is always the position of the
+//               starting node, not the current node, even if it is
+//               sampled during a traversal.  To get the render
+//               transform of the current node check in the current
+//               CullTraverserData.
+////////////////////////////////////////////////////////////////////
+INLINE const TransformState *qpCullTraverser::
+get_render_transform() const {
+  return _scene_setup->get_render_transform();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpCullTraverser::set_initial_state
 //       Access: Public
@@ -41,6 +94,31 @@ get_initial_state() const {
   return _initial_state;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::set_depth_offset_decals
+//       Access: Public
+//  Description: Sets the depth_offset_decals flag.  If this is true,
+//               decals will be rendered using DepthOffsetAttribs;
+//               otherwise, decals will be rendered with a more
+//               expensive three-pass system.  This is normally set
+//               from the corresponding flag in the GSG.
+////////////////////////////////////////////////////////////////////
+INLINE void qpCullTraverser::
+set_depth_offset_decals(bool flag) {
+  _depth_offset_decals = flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::get_depth_offset_decals
+//       Access: Public
+//  Description: Returns the depth_offset_decals flag.  See
+//               set_depth_offset_decals().
+////////////////////////////////////////////////////////////////////
+INLINE bool qpCullTraverser::
+get_depth_offset_decals() const {
+  return _depth_offset_decals;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpCullTraverser::set_camera_mask
 //       Access: Public
@@ -64,62 +142,6 @@ get_camera_mask() const {
   return _camera_mask;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpCullTraverser::set_camera_transform
-//       Access: Public
-//  Description: Specifies the position of the camera relative to the
-//               starting node, without any compensating
-//               coordinate-system transforms that might have been
-//               introduced for the purposes of rendering.
-////////////////////////////////////////////////////////////////////
-INLINE void qpCullTraverser::
-set_camera_transform(const TransformState *camera_transform) {
-  _camera_transform = camera_transform;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpCullTraverser::get_camera_transform
-//       Access: Public
-//  Description: Returns the position of the camera relative to the
-//               starting node, without any compensating
-//               coordinate-system transforms that might have been
-//               introduced for the purposes of rendering.
-////////////////////////////////////////////////////////////////////
-INLINE const TransformState *qpCullTraverser::
-get_camera_transform() const {
-  return _camera_transform;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpCullTraverser::set_render_transform
-//       Access: Public
-//  Description: Specifies the position of the starting node relative
-//               to the camera, pretransformed as appropriate for
-//               rendering.
-////////////////////////////////////////////////////////////////////
-INLINE void qpCullTraverser::
-set_render_transform(const TransformState *render_transform) {
-  _render_transform = render_transform;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpCullTraverser::get_render_transform
-//       Access: Public
-//  Description: Returns the position of the starting node relative
-//               to the camera, pretransformed as appropriate for
-//               rendering.
-//
-//               Note that this value is always the position of the
-//               starting node, not the current node, even if it is
-//               sampled during a traversal.  To get the render
-//               transform of the current node check in the current
-//               CullTraverserData.
-////////////////////////////////////////////////////////////////////
-INLINE const TransformState *qpCullTraverser::
-get_render_transform() const {
-  return _render_transform;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpCullTraverser::set_view_frustum
 //       Access: Public

+ 51 - 14
panda/src/pgraph/qpcullTraverser.cxx

@@ -30,6 +30,7 @@
 #include "colorAttrib.h"
 #include "renderModeAttrib.h"
 #include "cullFaceAttrib.h"
+#include "depthOffsetAttrib.h"
 
 
 TypeHandle qpCullTraverser::_type_handle;
@@ -42,9 +43,7 @@ TypeHandle qpCullTraverser::_type_handle;
 qpCullTraverser::
 qpCullTraverser() {
   _initial_state = RenderState::make_empty();
-  _camera_mask = DrawMask::all_on();
-  _camera_transform = DCAST(TransformState, TransformState::make_identity());
-  _render_transform = DCAST(TransformState, TransformState::make_identity());
+  _depth_offset_decals = false;
   _cull_handler = (CullHandler *)NULL;
 }
 
@@ -55,10 +54,9 @@ qpCullTraverser() {
 ////////////////////////////////////////////////////////////////////
 qpCullTraverser::
 qpCullTraverser(const qpCullTraverser &copy) :
+  _scene_setup(copy._scene_setup),
   _initial_state(copy._initial_state),
-  _camera_mask(copy._camera_mask),
-  _camera_transform(copy._camera_transform),
-  _render_transform(copy._render_transform),
+  _depth_offset_decals(copy._depth_offset_decals),
   _view_frustum(copy._view_frustum),
   _guard_band(copy._guard_band),
   _cull_handler(copy._cull_handler)
@@ -71,11 +69,12 @@ qpCullTraverser(const qpCullTraverser &copy) :
 //  Description: Begins the traversal from the indicated node.
 ////////////////////////////////////////////////////////////////////
 void qpCullTraverser::
-traverse(qpNodePath &root) {
+traverse(const qpNodePath &root) {
   nassertv(_cull_handler != (CullHandler *)NULL);
-
-  CullTraverserData data(root,
-                         _render_transform, TransformState::make_identity(),
+  nassertv(_scene_setup != (SceneSetup *)NULL);
+  
+  CullTraverserData data(root, get_render_transform(),
+                         TransformState::make_identity(),
                          _initial_state, _view_frustum, _guard_band);
   traverse(data);
 }
@@ -112,7 +111,7 @@ traverse(CullTraverserData &data) {
       // now.  This maybe isn't the perfect time to call it, but it's
       // good enough; and at this time we have all the information we
       // need for it.
-      fog->get_fog()->adjust_to_camera(_camera_transform);
+      fog->get_fog()->adjust_to_camera(get_camera_transform());
     }
 
     if (node->has_cull_callback()) {
@@ -133,10 +132,13 @@ traverse(CullTraverserData &data) {
 //               node's space.
 ////////////////////////////////////////////////////////////////////
 void qpCullTraverser::
-traverse_below(const CullTraverserData &data) {
+traverse_below(CullTraverserData &data) {
   PandaNode *node = data.node();
   const RenderEffects *node_effects = node->get_effects();
-  if (node_effects->has_decal()) {
+  bool has_decal = node_effects->has_decal();
+  if (has_decal && !_depth_offset_decals) {
+    // Start the three-pass decal rendering if we're not using
+    // DepthOffsetAttribs to implement decals.
     start_decal(data);
     
   } else {
@@ -150,7 +152,21 @@ traverse_below(const CullTraverserData &data) {
         _cull_handler->record_object(object);
       }
     }
-    
+
+    if (has_decal) {
+      // If we *are* implementing decals with DepthOffsetAttribs,
+      // apply it now, so that each child of this node gets offset by
+      // a tiny amount.
+      data._state = data._state->compose(get_depth_offset_state());
+#ifndef NDEBUG
+      // This is just a sanity check message.
+      if (!node->is_geom_node()) {
+        pgraph_cat.error()
+          << "DecalEffect applied to " << *node << ", not a GeomNode.\n";
+      }
+#endif
+    }
+
     // Now visit all the node's children.  We cannot use the
     // node->get_children() interface, because that will keep a read
     // pointer open, and we might end up changing this node during the
@@ -246,6 +262,7 @@ get_bounds_outer_viz_state() {
       (ColorAttrib::make_flat(Colorf(0.3f, 1.0f, 0.5f, 1.0f)),
        RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
        CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise));
+    state->ref();  // once more to guard against static destruction
   }
   return state;
 }
@@ -266,6 +283,26 @@ get_bounds_inner_viz_state() {
       (ColorAttrib::make_flat(Colorf(0.15f, 0.5f, 0.25f, 1.0f)),
        RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
        CullFaceAttrib::make(CullFaceAttrib::M_cull_counter_clockwise));
+    state->ref();  // once more to guard against static destruction
+  }
+  return state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::get_depth_offset_state
+//       Access: Private, Static
+//  Description: Returns a RenderState for increasing the DepthOffset
+//               by one.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) qpCullTraverser::
+get_depth_offset_state() {
+  // Once someone asks for this pointer, we hold its reference count
+  // and never free it.
+  static CPT(RenderState) state = (const RenderState *)NULL;
+  if (state == (const RenderState *)NULL) {
+    state = RenderState::make
+      (DepthOffsetAttrib::make(1));
+    state->ref();  // once more to guard against static destruction
   }
   return state;
 }

+ 16 - 12
panda/src/pgraph/qpcullTraverser.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 
 #include "geom.h"
+#include "sceneSetup.h"
 #include "renderState.h"
 #include "transformState.h"
 #include "geometricBoundingVolume.h"
@@ -48,18 +49,21 @@ public:
   qpCullTraverser();
   qpCullTraverser(const qpCullTraverser &copy);
 
-  INLINE void set_initial_state(const RenderState *initial_state);
-  INLINE const RenderState *get_initial_state() const;
+  INLINE void set_scene(SceneSetup *scene_setup);
+  INLINE SceneSetup *get_scene() const;
 
-  INLINE void set_camera_mask(const DrawMask &draw_mask);
+  INLINE void set_camera_mask(const DrawMask &camera_mask);
   INLINE const DrawMask &get_camera_mask() const;
 
-  INLINE void set_camera_transform(const TransformState *camera_transform);
   INLINE const TransformState *get_camera_transform() const;
-
-  INLINE void set_render_transform(const TransformState *render_transform);
   INLINE const TransformState *get_render_transform() const;
 
+  INLINE void set_initial_state(const RenderState *initial_state);
+  INLINE const RenderState *get_initial_state() const;
+
+  INLINE void set_depth_offset_decals(bool flag);
+  INLINE bool get_depth_offset_decals() const;
+
   INLINE void set_view_frustum(GeometricBoundingVolume *view_frustum);
   INLINE GeometricBoundingVolume *get_view_frustum() const;
 
@@ -69,24 +73,24 @@ public:
   INLINE void set_cull_handler(CullHandler *cull_handler);
   INLINE CullHandler *get_cull_handler() const;
 
-  void traverse(qpNodePath &root);
+  void traverse(const qpNodePath &root);
   void traverse(CullTraverserData &data);
-  void traverse_below(const CullTraverserData &data);
+  void traverse_below(CullTraverserData &data);
 
 private:
   void show_bounds(CullTraverserData &data);
   PT(Geom) make_bounds_viz(const BoundingVolume &vol);
   CPT(RenderState) get_bounds_outer_viz_state();
   CPT(RenderState) get_bounds_inner_viz_state();
-
+  CPT(RenderState) get_depth_offset_state();
   void start_decal(const CullTraverserData &data);
   CullableObject *r_get_decals(CullTraverserData &data,
                                CullableObject *decals);
 
-  CPT(RenderState) _initial_state;
+  PT(SceneSetup) _scene_setup;
   DrawMask _camera_mask;
-  CPT(TransformState) _camera_transform;
-  CPT(TransformState) _render_transform;
+  CPT(RenderState) _initial_state;
+  bool _depth_offset_decals;
   PT(GeometricBoundingVolume) _view_frustum;
   PT(GeometricBoundingVolume) _guard_band;
   CullHandler *_cull_handler;

+ 1 - 24
panda/src/pgraph/qplensNode.I

@@ -17,29 +17,6 @@
 ////////////////////////////////////////////////////////////////////
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpLensNode::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE qpLensNode::
-qpLensNode(const string &name) :
-  PandaNode(name)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpLensNode::Copy Constructor
-//       Access: Protected
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE qpLensNode::
-qpLensNode(const qpLensNode &copy) :
-  PandaNode(copy),
-  _lens(copy._lens)
-{
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: copy_lens
 //       Access: Public
@@ -73,6 +50,6 @@ set_lens(Lens *lens) {
 //               not yet a Lens associated.
 ////////////////////////////////////////////////////////////////////
 INLINE Lens *qpLensNode::
-get_lens() {
+get_lens() const {
   return _lens;
 }

+ 36 - 0
panda/src/pgraph/qplensNode.cxx

@@ -21,6 +21,42 @@
 
 TypeHandle qpLensNode::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpLensNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpLensNode::
+qpLensNode(const string &name) :
+  PandaNode(name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLensNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+qpLensNode::
+qpLensNode(const qpLensNode &copy) :
+  PandaNode(copy),
+  _lens(copy._lens)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpLensNode::xform
+//       Access: Public, Virtual
+//  Description: Transforms the contents of this PandaNode by the
+//               indicated matrix, if it means anything to do so.  For
+//               most kinds of PandaNodes, this does nothing.
+////////////////////////////////////////////////////////////////////
+void qpLensNode::
+xform(const LMatrix4f &mat) {
+  PandaNode::xform(mat);
+  // We need to actually transform the lens here.
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpLensNode::make_copy
 //       Access: Public, Virtual

+ 4 - 3
panda/src/pgraph/qplensNode.h

@@ -34,20 +34,21 @@
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA qpLensNode : public PandaNode {
 PUBLISHED:
-  INLINE qpLensNode(const string &name);
+  qpLensNode(const string &name);
 
 protected:
-  INLINE qpLensNode(const qpLensNode &copy);
+  qpLensNode(const qpLensNode &copy);
 public:
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
+  virtual void xform(const LMatrix4f &mat);
   virtual PandaNode *make_copy() const;
 
 PUBLISHED:
   INLINE void copy_lens(const Lens &lens);
   INLINE void set_lens(Lens *lens);
-  INLINE Lens *get_lens();
+  INLINE Lens *get_lens() const;
 
   bool is_in_view(const LPoint3f &pos);
 

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

@@ -119,6 +119,7 @@ RenderState::
       // as to the computed result (if any), so neither object will be
       // tempted to destruct.  Go ahead and remove ourselves from the
       // other cache.
+      nassertv(other->_composition_cache.count(this) == 1);
       other->_composition_cache.erase(this);
 
       // It's all right if the other state destructs now, since it

+ 145 - 0
panda/src/pgraph/sceneSetup.I

@@ -0,0 +1,145 @@
+// Filename: sceneSetup.I
+// Created by:  drose (27Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: SceneSetup::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE SceneSetup::
+SceneSetup() {
+  _camera_transform = TransformState::make_identity();
+  _render_transform = TransformState::make_identity();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::set_scene_root
+//       Access: Public
+//  Description: Specifies the root node of the scene.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneSetup::
+set_scene_root(const qpNodePath &scene_root) {
+  _scene_root = scene_root;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_scene_root
+//       Access: Public
+//  Description: Returns the root node of the scene.
+////////////////////////////////////////////////////////////////////
+INLINE const qpNodePath &SceneSetup::
+get_scene_root() const {
+  return _scene_root;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::set_camera
+//       Access: Public
+//  Description: Specifies the camera used to render the scene.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneSetup::
+set_camera(const qpCamera *camera) {
+  _camera = camera;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_camera
+//       Access: Public
+//  Description: Returns the camera used to render the scene.
+////////////////////////////////////////////////////////////////////
+INLINE const qpCamera *SceneSetup::
+get_camera() const {
+  return _camera;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::set_lens
+//       Access: Public
+//  Description: Indicates the particular Lens used for rendering.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneSetup::
+set_lens(const Lens *lens) {
+  _lens = lens;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_lens
+//       Access: Public
+//  Description: Returns the particular Lens used for rendering.
+////////////////////////////////////////////////////////////////////
+INLINE const Lens *SceneSetup::
+get_lens() const {
+  return _lens;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::set_camera_transform
+//       Access: Public
+//  Description: Specifies the position of the camera relative to the
+//               starting node, without any compensating
+//               coordinate-system transforms that might have been
+//               introduced for the purposes of rendering.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneSetup::
+set_camera_transform(const TransformState *camera_transform) {
+  _camera_transform = camera_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_camera_transform
+//       Access: Public
+//  Description: Returns the position of the camera relative to the
+//               starting node, without any compensating
+//               coordinate-system transforms that might have been
+//               introduced for the purposes of rendering.
+////////////////////////////////////////////////////////////////////
+INLINE const TransformState *SceneSetup::
+get_camera_transform() const {
+  return _camera_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::set_render_transform
+//       Access: Public
+//  Description: Specifies the position of the starting node relative
+//               to the camera, pretransformed as appropriate for
+//               rendering.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneSetup::
+set_render_transform(const TransformState *render_transform) {
+  _render_transform = render_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_render_transform
+//       Access: Public
+//  Description: Returns the position of the starting node relative
+//               to the camera, pretransformed as appropriate for
+//               rendering.
+//
+//               Note that this value is always the position of the
+//               starting node, not the current node, even if it is
+//               sampled during a traversal.  To get the render
+//               transform of the current node check in the current
+//               CullTraverserData.
+////////////////////////////////////////////////////////////////////
+INLINE const TransformState *SceneSetup::
+get_render_transform() const {
+  return _render_transform;
+}

+ 20 - 0
panda/src/pgraph/sceneSetup.cxx

@@ -0,0 +1,20 @@
+// Filename: sceneSetup.cxx
+// Created by:  drose (27Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "sceneSetup.h"
+

+ 66 - 0
panda/src/pgraph/sceneSetup.h

@@ -0,0 +1,66 @@
+// Filename: sceneSetup.h
+// Created by:  drose (27Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 SCENESETUP_H
+#define SCENESETUP_H
+
+#include "pandabase.h"
+
+#include "referenceCount.h"
+#include "qpnodePath.h"
+#include "qpcamera.h"
+#include "transformState.h"
+#include "lens.h"
+#include "pointerTo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : SceneSetup
+// Description : This object holds the camera position, etc., and
+//               other general setup information for rendering a
+//               particular scene.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA SceneSetup : public ReferenceCount {
+public:
+  INLINE SceneSetup();
+
+  INLINE void set_scene_root(const qpNodePath &scene_root);
+  INLINE const qpNodePath &get_scene_root() const;
+
+  INLINE void set_camera(const qpCamera *camera);
+  INLINE const qpCamera *get_camera() const;
+
+  INLINE void set_lens(const Lens *lens);
+  INLINE const Lens *get_lens() const;
+
+  INLINE void set_camera_transform(const TransformState *camera_transform);
+  INLINE const TransformState *get_camera_transform() const;
+
+  INLINE void set_render_transform(const TransformState *render_transform);
+  INLINE const TransformState *get_render_transform() const;
+
+private:
+  qpNodePath _scene_root;
+  CPT(qpCamera) _camera;
+  CPT(Lens) _lens;
+  CPT(TransformState) _camera_transform;
+  CPT(TransformState) _render_transform;
+};
+
+#include "sceneSetup.I"
+
+#endif

+ 0 - 3
panda/src/testbed/demo.cxx

@@ -482,9 +482,6 @@ event_L(CPT_Event) {
 }
 
 void demo_keys(EventHandler&) {
-  new RenderRelation( lights, dlight );
-  have_dlight = true;
-
   /*
   event_handler.add_hook("mw-in-label2d", event_in_label2d);
   event_handler.add_hook("mw-out-label2d", event_out_label2d);

+ 40 - 1
panda/src/testbed/pview.cxx

@@ -49,6 +49,9 @@
 #include "eventQueue.h"
 #include "eventHandler.h"
 #include "qpdataGraphTraverser.h"
+#include "lightAttrib.h"
+#include "ambientLight.h"
+#include "directionalLight.h"
 
 // Use dconfig to read a few Configrc variables.
 Configure(config_pview);
@@ -64,6 +67,7 @@ bool run_flag = true;
 // These are referenced in both event_w() and in event_b().
 bool wireframe = false;
 bool two_sided = false;
+bool lighting = false;
 
 
 // These are used by report_frame_rate().
@@ -76,6 +80,9 @@ static const int override_priority = 100;
 // This is the main scene graph.
 qpNodePath render;
 
+// And this is the camera node.
+qpNodePath camera_np;
+
 void 
 report_frame_rate() {
   double now = ClockObject::get_global_clock()->get_frame_time();
@@ -361,6 +368,37 @@ event_A(CPT_Event) {
 #endif
 }
 
+void
+event_l(CPT_Event) {
+  // 'l' : toggle lighting.
+  lighting = !lighting;
+  if (lighting) {
+    nout << "Enabling lighting.\n";
+    static bool lights_created = false;
+
+    static PT(AmbientLight) alight;
+    static PT(DirectionalLight) dlight;
+
+    if (!lights_created) {
+      alight = new AmbientLight("ambient");
+      alight->set_color(Colorf(0.2f, 0.2f, 0.2f, 1.0f));
+      dlight = new DirectionalLight("directional");
+      camera_np.attach_new_node(alight);
+      camera_np.attach_new_node(dlight);
+
+      lights_created = true;
+    }
+
+    // Enable lights on the top node.
+    render.node()->set_attrib(LightAttrib::make(LightAttrib::O_add, alight, dlight));
+
+  } else {
+    nout << "Disabling lighting.\n";
+    // Remove the lights from the top node.
+    render.node()->clear_attrib(LightAttrib::get_class_type());
+  }
+}
+
 int
 main(int argc, char *argv[]) {
   // First, we need a GraphicsPipe, before we can open a window.
@@ -375,7 +413,7 @@ main(int argc, char *argv[]) {
 
   // Now we just need to make a scene graph for the camera to render.
   render = qpNodePath("render");
-  render.attach_new_node(camera);
+  camera_np = render.attach_new_node(camera);
   camera->set_scene(render);
 
   // This is maybe here temporarily, and maybe not.
@@ -398,6 +436,7 @@ main(int argc, char *argv[]) {
   event_handler.add_hook("s", event_s);
   event_handler.add_hook("shift-s", event_S);
   event_handler.add_hook("shift-a", event_A);
+  event_handler.add_hook("l", event_l);
 
 
   // Put something in the scene graph to look at.