Browse Source

use new munger system for qpgeom sprites/points

David Rose 21 years ago
parent
commit
43f4675d65
72 changed files with 1435 additions and 685 deletions
  1. 2 2
      panda/src/collide/collisionVisualizer.cxx
  2. 3 1
      panda/src/display/graphicsEngine.cxx
  3. 21 10
      panda/src/display/graphicsStateGuardian.cxx
  4. 3 1
      panda/src/display/graphicsStateGuardian.h
  5. 10 4
      panda/src/display/standardMunger.cxx
  6. 2 0
      panda/src/display/standardMunger.h
  7. 15 7
      panda/src/doc/eggSyntax.txt
  8. 2 2
      panda/src/dxgsg8/dxGraphicsStateGuardian8.I
  9. 4 4
      panda/src/egg/eggLine.I
  10. 1 1
      panda/src/egg/eggLine.h
  11. 61 16
      panda/src/egg/eggPoint.I
  12. 5 0
      panda/src/egg/eggPoint.cxx
  13. 13 2
      panda/src/egg/eggPoint.h
  14. 6 0
      panda/src/egg/eggTexture.cxx
  15. 2 0
      panda/src/egg/eggTexture.h
  16. 6 0
      panda/src/egg/parser.yxx
  17. 6 2
      panda/src/egg2pg/eggRenderState.cxx
  18. 0 11
      panda/src/glstuff/glGraphicsStateGuardian_src.I
  19. 38 195
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  20. 0 13
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  21. 0 3
      panda/src/gobj/Sources.pp
  22. 0 3
      panda/src/gobj/config_gobj.cxx
  23. 0 1
      panda/src/gobj/gobj_composite1.cxx
  24. 12 12
      panda/src/gobj/internalName.I
  25. 2 2
      panda/src/gobj/internalName.cxx
  26. 4 4
      panda/src/gobj/internalName.h
  27. 24 2
      panda/src/gobj/qpgeom.I
  28. 41 6
      panda/src/gobj/qpgeom.cxx
  29. 31 1
      panda/src/gobj/qpgeom.h
  30. 8 5
      panda/src/gobj/qpgeomMunger.cxx
  31. 1 1
      panda/src/gobj/qpgeomMunger.h
  32. 0 148
      panda/src/gobj/qpgeomSprites.cxx
  33. 0 90
      panda/src/gobj/qpgeomSprites.h
  34. 1 0
      panda/src/gobj/qpgeomVertexColumn.h
  35. 51 0
      panda/src/gobj/qpgeomVertexData.I
  36. 78 0
      panda/src/gobj/qpgeomVertexData.cxx
  37. 18 0
      panda/src/gobj/qpgeomVertexData.h
  38. 90 0
      panda/src/gobj/qpgeomVertexFormat.I
  39. 14 1
      panda/src/gobj/qpgeomVertexFormat.cxx
  40. 14 0
      panda/src/gobj/qpgeomVertexFormat.h
  41. 4 5
      panda/src/gsgbase/graphicsStateGuardianBase.h
  42. 4 4
      panda/src/parametrics/ropeNode.cxx
  43. 2 2
      panda/src/parametrics/sheetNode.cxx
  44. 2 1
      panda/src/particlesystem/ringEmitter.cxx
  45. 45 3
      panda/src/particlesystem/spriteParticleRenderer.I
  46. 157 87
      panda/src/particlesystem/spriteParticleRenderer.cxx
  47. 9 5
      panda/src/particlesystem/spriteParticleRenderer.h
  48. 2 2
      panda/src/pgraph/binCullHandler.cxx
  49. 2 1
      panda/src/pgraph/binCullHandler.h
  50. 1 1
      panda/src/pgraph/cullHandler.cxx
  51. 4 1
      panda/src/pgraph/cullHandler.h
  52. 4 3
      panda/src/pgraph/cullResult.cxx
  53. 2 1
      panda/src/pgraph/cullResult.h
  54. 10 0
      panda/src/pgraph/cullTraverser.I
  55. 8 5
      panda/src/pgraph/cullTraverser.cxx
  56. 5 1
      panda/src/pgraph/cullTraverser.h
  57. 22 0
      panda/src/pgraph/cullableObject.I
  58. 312 7
      panda/src/pgraph/cullableObject.cxx
  59. 23 1
      panda/src/pgraph/cullableObject.h
  60. 2 2
      panda/src/pgraph/drawCullHandler.cxx
  61. 2 1
      panda/src/pgraph/drawCullHandler.h
  62. 24 0
      panda/src/pgraph/renderModeAttrib.I
  63. 3 0
      panda/src/pgraph/renderModeAttrib.h
  64. 39 0
      panda/src/pgraph/renderState.I
  65. 15 0
      panda/src/pgraph/renderState.cxx
  66. 9 0
      panda/src/pgraph/renderState.h
  67. 57 0
      panda/src/pgraph/sceneSetup.I
  68. 10 0
      panda/src/pgraph/sceneSetup.h
  69. 24 2
      panda/src/pgraph/texGenAttrib.I
  70. 29 0
      panda/src/pgraph/texGenAttrib.cxx
  71. 13 0
      panda/src/pgraph/texGenAttrib.h
  72. 1 0
      panda/src/pstatclient/pStatProperties.cxx

+ 2 - 2
panda/src/collide/collisionVisualizer.cxx

@@ -191,7 +191,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
           CullableObject *object = 
           CullableObject *object = 
             new CullableObject(sphere, empty_state, xform_data._render_transform);
             new CullableObject(sphere, empty_state, xform_data._render_transform);
           
           
-          trav->get_cull_handler()->record_object(object);
+          trav->get_cull_handler()->record_object(object, trav);
         }
         }
 
 
         // Draw the normal vector at the surface point.
         // Draw the normal vector at the surface point.
@@ -209,7 +209,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
           CullableObject *object = 
           CullableObject *object = 
             new CullableObject(line, empty_state, xform_data._render_transform);
             new CullableObject(line, empty_state, xform_data._render_transform);
           
           
-          trav->get_cull_handler()->record_object(object);
+          trav->get_cull_handler()->record_object(object, trav);
         }
         }
       }
       }
     }
     }

+ 3 - 1
panda/src/display/graphicsEngine.cxx

@@ -1054,6 +1054,8 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
     initial_state = initial_state->compose(get_invert_polygon_state());
     initial_state = initial_state->compose(get_invert_polygon_state());
   }
   }
 
 
+  scene_setup->set_display_region(dr);
+  scene_setup->set_viewport_size(dr->get_pixel_width(), dr->get_pixel_height());
   scene_setup->set_scene_root(scene_root);
   scene_setup->set_scene_root(scene_root);
   scene_setup->set_camera_path(camera);
   scene_setup->set_camera_path(camera);
   scene_setup->set_camera_node(camera_node);
   scene_setup->set_camera_node(camera_node);
@@ -1077,7 +1079,7 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
   // Statistics
   // Statistics
   PStatTimer timer(_cull_pcollector);
   PStatTimer timer(_cull_pcollector);
 
 
-  CullTraverser trav;
+  CullTraverser trav(gsg);
   trav.set_cull_handler(cull_handler);
   trav.set_cull_handler(cull_handler);
   trav.set_depth_offset_decals(depth_offset_decals && gsg->depth_offset_decals());
   trav.set_depth_offset_decals(depth_offset_decals && gsg->depth_offset_decals());
   trav.set_scene(scene_setup);
   trav.set_scene(scene_setup);

+ 21 - 10
panda/src/display/graphicsStateGuardian.cxx

@@ -116,6 +116,8 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
   _supports_multisample = false;
   _supports_multisample = false;
   _supports_generate_mipmap = false;
   _supports_generate_mipmap = false;
   _supports_render_texture = false;
   _supports_render_texture = false;
+
+  _supported_point_rendering = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -145,6 +147,23 @@ get_supports_multisample() const {
   return _supports_multisample;
   return _supports_multisample;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supported_point_rendering
+//       Access: Published, Virtual
+//  Description: Returns the union of Geom::PointRendering values that
+//               this particular GSG can support directly.  If a point
+//               needs to be rendered that requires some additional
+//               properties, the StandardMunger will convert it into
+//               quads instead.
+//
+//               This method is declared virtual solely so that it can
+//               be queried from cullableObject.cxx.
+////////////////////////////////////////////////////////////////////
+int GraphicsStateGuardian::
+get_supported_point_rendering() const {
+  return _supported_point_rendering;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::reset
 //     Function: GraphicsStateGuardian::reset
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -679,7 +698,8 @@ begin_draw_primitives(const qpGeom *, const qpGeomMunger *munger,
                       const qpGeomVertexData *data) {
                       const qpGeomVertexData *data) {
   _munger = munger;
   _munger = munger;
   _vertex_data = data;
   _vertex_data = data;
-  return true;
+
+  return _vertex_data->has_vertex();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -748,15 +768,6 @@ void GraphicsStateGuardian::
 draw_points(const qpGeomPoints *) {
 draw_points(const qpGeomPoints *) {
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::draw_sprites
-//       Access: Public, Virtual
-//  Description: Draws a series of rectangular sprite polygons.
-////////////////////////////////////////////////////////////////////
-void GraphicsStateGuardian::
-draw_sprites(const qpGeomSprites *) {
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::end_draw_primitives()
 //     Function: GraphicsStateGuardian::end_draw_primitives()
 //       Access: Public, Virtual
 //       Access: Public, Virtual

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

@@ -96,6 +96,8 @@ PUBLISHED:
   INLINE bool get_supports_generate_mipmap() const;
   INLINE bool get_supports_generate_mipmap() const;
   INLINE bool get_supports_render_texture() const;
   INLINE bool get_supports_render_texture() const;
 
 
+  virtual int get_supported_point_rendering() const;
+
 public:
 public:
   INLINE bool set_scene(SceneSetup *scene_setup);
   INLINE bool set_scene(SceneSetup *scene_setup);
   INLINE SceneSetup *get_scene() const;
   INLINE SceneSetup *get_scene() const;
@@ -160,7 +162,6 @@ public:
   virtual void draw_lines(const qpGeomLines *primitive);
   virtual void draw_lines(const qpGeomLines *primitive);
   virtual void draw_linestrips(const qpGeomLinestrips *primitive);
   virtual void draw_linestrips(const qpGeomLinestrips *primitive);
   virtual void draw_points(const qpGeomPoints *primitive);
   virtual void draw_points(const qpGeomPoints *primitive);
-  virtual void draw_sprites(const qpGeomSprites *primitive);
   virtual void end_draw_primitives();
   virtual void end_draw_primitives();
 
 
   virtual bool framebuffer_bind_to_texture(GraphicsOutput *win, Texture *tex);
   virtual bool framebuffer_bind_to_texture(GraphicsOutput *win, Texture *tex);
@@ -325,6 +326,7 @@ protected:
   bool _supports_multisample;
   bool _supports_multisample;
   bool _supports_generate_mipmap;
   bool _supports_generate_mipmap;
   bool _supports_render_texture;
   bool _supports_render_texture;
+  int _supported_point_rendering;
 
 
 public:
 public:
   // Statistics
   // Statistics

+ 10 - 4
panda/src/display/standardMunger.cxx

@@ -45,6 +45,7 @@ StandardMunger(const GraphicsStateGuardianBase *gsg, const RenderState *state,
   _gsg = DCAST(GraphicsStateGuardian, gsg);
   _gsg = DCAST(GraphicsStateGuardian, gsg);
   _color = state->get_color();
   _color = state->get_color();
   _color_scale = state->get_color_scale();
   _color_scale = state->get_color_scale();
+  _render_mode = state->get_render_mode();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -149,6 +150,9 @@ compare_to_impl(const qpGeomMunger *other) const {
   if (_color_scale != om->_color_scale) {
   if (_color_scale != om->_color_scale) {
     return _color_scale < om->_color_scale ? -1 : 1;
     return _color_scale < om->_color_scale ? -1 : 1;
   }
   }
+  if (_render_mode != om->_render_mode) {
+    return _render_mode < om->_render_mode ? -1 : 1;
+  }
 
 
   return qpGeomMunger::compare_to_impl(other);
   return qpGeomMunger::compare_to_impl(other);
 }
 }
@@ -156,10 +160,12 @@ compare_to_impl(const qpGeomMunger *other) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: StandardMunger::geom_compare_to_impl
 //     Function: StandardMunger::geom_compare_to_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
-//  Description: Called to compare two GeomMungers who are known to be
-//               of the same type, for an apples-to-apples comparison.
-//               This will never be called on two pointers of a
-//               different type.
+//  Description: Compares two GeomMungers, considering only whether
+//               they would produce a different answer to
+//               munge_format(), munge_data(), or munge_geom().  (They
+//               still might be different in other ways, but if they
+//               would produce the same answer, this function consider
+//               them to be the same.)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int StandardMunger::
 int StandardMunger::
 geom_compare_to_impl(const qpGeomMunger *other) const {
 geom_compare_to_impl(const qpGeomMunger *other) const {

+ 2 - 0
panda/src/display/standardMunger.h

@@ -24,6 +24,7 @@
 #include "graphicsStateGuardian.h"
 #include "graphicsStateGuardian.h"
 #include "colorAttrib.h"
 #include "colorAttrib.h"
 #include "colorScaleAttrib.h"
 #include "colorScaleAttrib.h"
+#include "renderModeAttrib.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -55,6 +56,7 @@ private:
   CPT(GraphicsStateGuardian) _gsg;
   CPT(GraphicsStateGuardian) _gsg;
   CPT(ColorAttrib) _color;
   CPT(ColorAttrib) _color;
   CPT(ColorScaleAttrib) _color_scale;
   CPT(ColorScaleAttrib) _color_scale;
+  CPT(RenderModeAttrib) _render_mode;
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 15 - 7
panda/src/doc/eggSyntax.txt

@@ -287,6 +287,7 @@ appear before they are referenced.
       WORLD_POSITION
       WORLD_POSITION
       OBJECT_POSITION
       OBJECT_POSITION
       EYE_POSITION
       EYE_POSITION
+      POINT_SPRITE
 
 
   <Scalar> stage-name { name }
   <Scalar> stage-name { name }
 
 
@@ -696,8 +697,15 @@ GEOMETRY ENTRIES
 
 
     This specifies the size of the PointLight (or the width of a
     This specifies the size of the PointLight (or the width of a
     line), in pixels, when it is rendered.  This may be a
     line), in pixels, when it is rendered.  This may be a
-    floating-point number, which is meaningful only when antialiasing
-    is in effect.  The default is 1.0.
+    floating-point number, but the fractional part is meaningful only
+    when antialiasing is in effect.  The default is 1.0.
+
+  <Scalar> perspective { boolean-value }
+
+    If this is specified, then the thickness, above, is to interpreted
+    as a size in 3-d spatial units, rather than a size in pixels, and
+    the point should be scaled according to its distance from the
+    viewer normally.
 
 
 
 
 
 
@@ -715,11 +723,11 @@ GEOMETRY ENTRIES
   vertex 1, vertex 1 and vertex 2, etc.  The line is not implicitly
   vertex 1, vertex 1 and vertex 2, etc.  The line is not implicitly
   closed; if you wish to represent a loop, you must repeat vertex 0 at
   closed; if you wish to represent a loop, you must repeat vertex 0 at
   the end.  As with a PointLight, normals, textures, colors,
   the end.  As with a PointLight, normals, textures, colors,
-  draw_order, and the "thick" attribute are all valid.  Also, since a
-  Line (with more than two vertices) is made up of multiple line
-  segments, it may contain a number of <Component> entries, to set a
-  different color and/or normal for each line segment, as in
-  TriangleStrip, below.
+  draw_order, and the "thick" attribute are all valid (but not
+  "perspective").  Also, since a Line (with more than two vertices) is
+  made up of multiple line segments, it may contain a number of
+  <Component> entries, to set a different color and/or normal for each
+  line segment, as in TriangleStrip, below.
 
 
 
 
 <TriangleStrip> name { 
 <TriangleStrip> name { 

+ 2 - 2
panda/src/dxgsg8/dxGraphicsStateGuardian8.I

@@ -474,8 +474,8 @@ wants_texcoords() const {
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: This function may only be called during a render
 //  Description: This function may only be called during a render
 //               traversal; it will compute the distance to the
 //               traversal; it will compute the distance to the
-//               indicated point, assumed to be in modelview
-//               coordinates, from the camera plane.
+//               indicated point, assumed to be in eye coordinates,
+//               from the camera plane.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float DXGraphicsStateGuardian8::
 INLINE float DXGraphicsStateGuardian8::
 compute_distance_to(const LPoint3f &point) const {
 compute_distance_to(const LPoint3f &point) const {

+ 4 - 4
panda/src/egg/eggLine.I

@@ -68,8 +68,8 @@ has_thick() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLine::get_thick
 //     Function: EggLine::get_thick
 //       Access: Published
 //       Access: Published
-//  Description: Returns the thick set on this particular attribute.
-//               If there is no thick set, returns white.
+//  Description: Returns the thickness set on this particular line.
+//               If there is no thickness set, returns 1.0.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE double EggLine::
 INLINE double EggLine::
 get_thick() const {
 get_thick() const {
@@ -81,7 +81,7 @@ get_thick() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: EggLine::
+//     Function: EggLine::set_thick
 //       Access: Published
 //       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -92,7 +92,7 @@ set_thick(double thick) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: EggLine::
+//     Function: EggLine::clear_thick
 //       Access: Published
 //       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/egg/eggLine.h

@@ -39,7 +39,7 @@ PUBLISHED:
 
 
   INLINE bool has_thick() const;
   INLINE bool has_thick() const;
   INLINE double get_thick() const;
   INLINE double get_thick() const;
-  INLINE void set_thick(const double thick);
+  INLINE void set_thick(double thick);
   INLINE void clear_thick();
   INLINE void clear_thick();
 
 
 protected:
 protected:

+ 61 - 16
panda/src/egg/eggPoint.I

@@ -25,7 +25,8 @@
 INLINE EggPoint::
 INLINE EggPoint::
 EggPoint(const string &name) : 
 EggPoint(const string &name) : 
   EggPrimitive(name),
   EggPrimitive(name),
-  _has_thick(false)
+  _flags(0),
+  _thick(1.0)
 {
 {
 }
 }
 
 
@@ -37,8 +38,8 @@ EggPoint(const string &name) :
 INLINE EggPoint::
 INLINE EggPoint::
 EggPoint(const EggPoint &copy) : 
 EggPoint(const EggPoint &copy) : 
   EggPrimitive(copy),
   EggPrimitive(copy),
-  _thick(copy._thick),
-  _has_thick(copy._has_thick)
+  _flags(copy._flags),
+  _thick(copy._thick)
 {
 {
 }
 }
 
 
@@ -50,8 +51,8 @@ EggPoint(const EggPoint &copy) :
 INLINE EggPoint &EggPoint::
 INLINE EggPoint &EggPoint::
 operator = (const EggPoint &copy) {
 operator = (const EggPoint &copy) {
   EggPrimitive::operator = (copy);
   EggPrimitive::operator = (copy);
+  _flags = copy._flags;
   _thick = copy._thick;
   _thick = copy._thick;
-  _has_thick = copy._has_thick;
   return *this;
   return *this;
 }
 }
 
 
@@ -62,41 +63,85 @@ operator = (const EggPoint &copy) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool EggPoint::
 INLINE bool EggPoint::
 has_thick() const {
 has_thick() const {
-  return _has_thick;
+  return (_flags & F_has_thick) != 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPoint::get_thick
 //     Function: EggPoint::get_thick
 //       Access: Published
 //       Access: Published
-//  Description: Returns the thick set on this particular attribute.
-//               If there is no thick set, returns white.
+//  Description: Returns the thickness set on this particular point.
+//               If there is no thickness set, returns 1.0.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE double EggPoint::
 INLINE double EggPoint::
 get_thick() const {
 get_thick() const {
-  if (has_thick()) {
-    return _thick;
-  } else {
-    return 1.0;
-  }
+  return _thick;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: EggPoint::
+//     Function: EggPoint::set_thick
 //       Access: Published
 //       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void EggPoint::
 INLINE void EggPoint::
 set_thick(double thick) {
 set_thick(double thick) {
   _thick = thick;
   _thick = thick;
-  _has_thick = true;
+  _flags |= F_has_thick;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: EggPoint::
+//     Function: EggPoint::clear_thick
 //       Access: Published
 //       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void EggPoint::
 INLINE void EggPoint::
 clear_thick() {
 clear_thick() {
-  _has_thick = false;
+  _thick = 1.0;
+  _flags &= ~F_has_thick;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPoint::has_perspective
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool EggPoint::
+has_perspective() const {
+  return (_flags & F_has_perspective) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPoint::get_perspective
+//       Access: Published
+//  Description: Returns the perspective flag set on this particular
+//               point.  If there is no perspective flag set, returns
+//               false.
+////////////////////////////////////////////////////////////////////
+INLINE bool EggPoint::
+get_perspective() const {
+  return (_flags & F_perspective) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPoint::set_perspective
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void EggPoint::
+set_perspective(bool perspective) {
+  if (perspective) {
+    _flags |= F_perspective;
+  } else {
+    _flags &= ~F_perspective;
+  }
+  _flags |= F_has_perspective;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPoint::clear_perspective
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void EggPoint::
+clear_perspective() {
+  _flags &= ~(F_has_perspective | F_perspective);
 }
 }

+ 5 - 0
panda/src/egg/eggPoint.cxx

@@ -53,6 +53,11 @@ write(ostream &out, int indent_level) const {
       << "<Scalar> thick { " << get_thick() << " }\n";
       << "<Scalar> thick { " << get_thick() << " }\n";
   }
   }
 
 
+  if (has_perspective()) {
+    indent(out, indent_level + 2) 
+      << "<Scalar> perspective { " << get_perspective() << " }\n";
+  }
+
   write_body(out, indent_level + 2);
   write_body(out, indent_level + 2);
   indent(out, indent_level) << "}\n";
   indent(out, indent_level) << "}\n";
 }
 }

+ 13 - 2
panda/src/egg/eggPoint.h

@@ -36,16 +36,27 @@ PUBLISHED:
 
 
   INLINE bool has_thick() const;
   INLINE bool has_thick() const;
   INLINE double get_thick() const;
   INLINE double get_thick() const;
-  INLINE void set_thick(const double thick);
+  INLINE void set_thick(double thick);
   INLINE void clear_thick();
   INLINE void clear_thick();
 
 
+  INLINE bool has_perspective() const;
+  INLINE bool get_perspective() const;
+  INLINE void set_perspective(bool perspective);
+  INLINE void clear_perspective();
+
   virtual bool cleanup();
   virtual bool cleanup();
 
 
   virtual void write(ostream &out, int indent_level) const;
   virtual void write(ostream &out, int indent_level) const;
 
 
 private:
 private:
+  enum Flags {
+    F_has_thick       = 0x0001,
+    F_perspective     = 0x0002,
+    F_has_perspective = 0x0004,
+  };
+
+  int _flags;
   double _thick;
   double _thick;
-  bool _has_thick;
 
 
 public:
 public:
 
 

+ 6 - 0
panda/src/egg/eggTexture.cxx

@@ -832,6 +832,9 @@ string_tex_gen(const string &string) {
   } else if (cmp_nocase_uh(string, "eye_position") == 0) {
   } else if (cmp_nocase_uh(string, "eye_position") == 0) {
     return TG_eye_position;
     return TG_eye_position;
 
 
+  } else if (cmp_nocase_uh(string, "point_sprite") == 0) {
+    return TG_point_sprite;
+
   } else {
   } else {
     return TG_unspecified;
     return TG_unspecified;
   }
   }
@@ -1150,6 +1153,9 @@ operator << (ostream &out, EggTexture::TexGen tex_gen) {
 
 
   case EggTexture::TG_eye_position:
   case EggTexture::TG_eye_position:
     return out << "eye_position";
     return out << "eye_position";
+
+  case EggTexture::TG_point_sprite:
+    return out << "point_sprite";
   }
   }
 
 
   return out << "**invalid TexGen(" << (int)tex_gen << ")**";
   return out << "**invalid TexGen(" << (int)tex_gen << ")**";

+ 2 - 0
panda/src/egg/eggTexture.h

@@ -141,6 +141,8 @@ PUBLISHED:
     TG_world_position,
     TG_world_position,
     TG_object_position,
     TG_object_position,
     TG_eye_position,
     TG_eye_position,
+
+    TG_point_sprite,
   };
   };
 
 
   INLINE void set_format(Format format);
   INLINE void set_format(Format format);

+ 6 - 0
panda/src/egg/parser.yxx

@@ -1821,6 +1821,12 @@ primitive_body:
     } else {
     } else {
       eggyywarning("scalar thick is only meaningful for points and lines.");
       eggyywarning("scalar thick is only meaningful for points and lines.");
     }
     }
+  } else if (cmp_nocase_uh(name, "perspective") == 0) {
+    if (primitive->is_of_type(EggPoint::get_class_type())) {
+      DCAST(EggPoint, primitive)->set_perspective(value);
+    } else {
+      eggyywarning("scalar perspective is only meaningful for points.");
+    }
   } else {
   } else {
     eggyywarning("Unknown scalar " + name);
     eggyywarning("Unknown scalar " + name);
   }
   }

+ 6 - 2
panda/src/egg2pg/eggRenderState.cxx

@@ -362,9 +362,10 @@ fill_state(EggPrimitive *egg_prim) {
   } else if (egg_prim->is_of_type(EggPoint::get_class_type())) {
   } else if (egg_prim->is_of_type(EggPoint::get_class_type())) {
     _primitive_type = qpGeomPrimitive::PT_points;
     _primitive_type = qpGeomPrimitive::PT_points;
     EggPoint *egg_point = DCAST(EggPoint, egg_prim);
     EggPoint *egg_point = DCAST(EggPoint, egg_prim);
-    if (egg_point->get_thick() != 1.0) {
+    if (egg_point->get_thick() != 1.0 || egg_point->get_perspective()) {
       add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, 
       add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, 
-                                        egg_point->get_thick()));
+                                        egg_point->get_thick(),
+                                        egg_point->get_perspective()));
     }
     }
   } else {
   } else {
     _primitive_type = qpGeomPrimitive::PT_polygons;
     _primitive_type = qpGeomPrimitive::PT_polygons;
@@ -476,6 +477,9 @@ get_tex_gen(const EggTexture *egg_tex) {
 
 
   case EggTexture::TG_eye_position:
   case EggTexture::TG_eye_position:
     return TexGenAttrib::M_eye_position;
     return TexGenAttrib::M_eye_position;
+
+  case EggTexture::TG_point_sprite:
+    return TexGenAttrib::M_point_sprite;
   };
   };
 
 
   return TexGenAttrib::M_off;
   return TexGenAttrib::M_off;

+ 0 - 11
panda/src/glstuff/glGraphicsStateGuardian_src.I

@@ -526,14 +526,3 @@ issue_flat_shading(Geom *geom) {
     // Otherwise, it's a uniform-colored primitive; we can take either one.
     // Otherwise, it's a uniform-colored primitive; we can take either one.
   }
   }
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: CLP(GraphicsStateGuardian)::SpriteData::Ordering
-//       Access: Public
-//  Description: Orders the sprites from back-to-front for correct
-//               transparency sorting in draw_sprites().
-////////////////////////////////////////////////////////////////////
-INLINE bool CLP(GraphicsStateGuardian)::SpriteData::
-operator < (const SpriteData &other) const {
-  return _eye[2] < other._eye[2];
-}

+ 38 - 195
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -28,7 +28,6 @@
 #include "qpgeomLines.h"
 #include "qpgeomLines.h"
 #include "qpgeomLinestrips.h"
 #include "qpgeomLinestrips.h"
 #include "qpgeomPoints.h"
 #include "qpgeomPoints.h"
-#include "qpgeomSprites.h"
 #include "qpgeomVertexReader.h"
 #include "qpgeomVertexReader.h"
 #include "graphicsWindow.h"
 #include "graphicsWindow.h"
 #include "lens.h"
 #include "lens.h"
@@ -2278,6 +2277,18 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomMunger *munger,
     }
     }
   }
   }
 
 
+  if (_vertex_data->is_vertex_transformed()) {
+    // If the vertex data claims to be already transformed into clip
+    // coordinates, wipe out the current projection and modelview
+    // matrix (so we don't attempt to transform it again).
+    GLP(MatrixMode)(GL_PROJECTION);
+    GLP(PushMatrix)();
+    GLP(LoadIdentity)();
+    GLP(MatrixMode)(GL_MODELVIEW);
+    GLP(PushMatrix)();
+    GLP(LoadIdentity)();
+  }
+
   if (geom->get_usage_hint() == qpGeomUsageHint::UH_static && 
   if (geom->get_usage_hint() == qpGeomUsageHint::UH_static && 
       _vertex_data->get_usage_hint() == qpGeomUsageHint::UH_static &&
       _vertex_data->get_usage_hint() == qpGeomUsageHint::UH_static &&
       display_lists && (!hardware_animation || display_list_animation)) {
       display_lists && (!hardware_animation || display_list_animation)) {
@@ -2335,18 +2346,16 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomMunger *munger,
   int start;
   int start;
   int stride;
   int stride;
 
 
-  if (_vertex_data->get_array_info(InternalName::get_vertex(),
-                                   array_data, num_values, numeric_type, 
-                                   start, stride)) {
+  if (_vertex_data->get_vertex_info(array_data, num_values, numeric_type, 
+                                    start, stride)) {
     const unsigned char *client_pointer = setup_array_data(array_data);
     const unsigned char *client_pointer = setup_array_data(array_data);
     GLP(VertexPointer)(num_values, get_numeric_type(numeric_type), 
     GLP(VertexPointer)(num_values, get_numeric_type(numeric_type), 
                        stride, client_pointer + start);
                        stride, client_pointer + start);
     GLP(EnableClientState)(GL_VERTEX_ARRAY);
     GLP(EnableClientState)(GL_VERTEX_ARRAY);
   }
   }
 
 
-  if (_vertex_data->get_array_info(InternalName::get_normal(),
-                                   array_data, num_values, numeric_type, 
-                                   start, stride)) {
+  if (_vertex_data->get_normal_info(array_data, numeric_type, 
+                                    start, stride)) {
     const unsigned char *client_pointer = setup_array_data(array_data);
     const unsigned char *client_pointer = setup_array_data(array_data);
     GLP(NormalPointer)(get_numeric_type(numeric_type), stride, 
     GLP(NormalPointer)(get_numeric_type(numeric_type), stride, 
                        client_pointer + start);
                        client_pointer + start);
@@ -2355,8 +2364,7 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomMunger *munger,
     GLP(DisableClientState)(GL_NORMAL_ARRAY);
     GLP(DisableClientState)(GL_NORMAL_ARRAY);
   }
   }
 
 
-  if (_vertex_data->get_array_info(InternalName::get_color(),
-                                   array_data, num_values, numeric_type, 
+  if (_vertex_data->get_color_info(array_data, num_values, numeric_type, 
                                    start, stride) &&
                                    start, stride) &&
       numeric_type != qpGeomVertexColumn::NT_packed_dabc) {
       numeric_type != qpGeomVertexColumn::NT_packed_dabc) {
     const unsigned char *client_pointer = setup_array_data(array_data);
     const unsigned char *client_pointer = setup_array_data(array_data);
@@ -2554,169 +2562,6 @@ draw_points(const qpGeomPoints *primitive) {
   report_my_gl_errors();
   report_my_gl_errors();
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: CLP(GraphicsStateGuardian)::draw_sprites
-//       Access: Public, Virtual
-//  Description: Draws a series of rectangular sprite polygons.
-////////////////////////////////////////////////////////////////////
-void CLP(GraphicsStateGuardian)::
-draw_sprites(const qpGeomSprites *primitive) {
-  bool has_rotate = _vertex_data->has_column(InternalName::get_rotate());
-  bool has_scale_x = _vertex_data->has_column(InternalName::get_scale_x());
-  bool has_scale_y = _vertex_data->has_column(InternalName::get_scale_y());
-  if (!_supports_point_sprite || 
-      has_rotate || has_scale_x || has_scale_y) {
-    // In this case, we have to draw the sprites as a series of
-    // polygons, because either the GL doesn't support the
-    // point_sprite extension, or the vertex data requires some
-    // nonstandard adjustments not covered by point_sprite.
-
-    // This is kind of a shame because we have already prepared the
-    // vertex buffer, and it turns out we're not going to use it.  Oh
-    // well, too bad.
-
-    // Now clear the projection and modelview matrices, since we'll do
-    // the transformation to camera space ourselves.
-    GLP(MatrixMode)(GL_PROJECTION);
-    GLP(PushMatrix)();
-    GLP(LoadIdentity)();
-    GLP(MatrixMode)(GL_MODELVIEW);
-    GLP(PushMatrix)();
-    GLP(LoadIdentity)();
-
-    bool has_color = _vertex_data->has_column(InternalName::get_color());
-
-    // First, get all of the points in eye space.
-    const LMatrix4f &modelview = _transform->get_mat();
-    qpGeomVertexReader vertex(_vertex_data, InternalName::get_vertex());
-    qpGeomVertexReader color(_vertex_data, InternalName::get_color());
-    qpGeomVertexReader rotate(_vertex_data, InternalName::get_rotate());
-    qpGeomVertexReader scale_x(_vertex_data, InternalName::get_scale_x());
-    qpGeomVertexReader scale_y(_vertex_data, InternalName::get_scale_y());
-
-    int num_sprites = primitive->get_num_vertices();
-    SpriteData *sprites = (SpriteData *)alloca(num_sprites * sizeof(SpriteData));
-    SpriteData **sprite_pointers = (SpriteData **)alloca(num_sprites * sizeof(SpriteData *));
-
-    SpriteData *sd = sprites;
-    SpriteData *sd_end = sprites + num_sprites;
-    SpriteData **sdp = sprite_pointers;
-    SpriteData **sdp_end = sprite_pointers + num_sprites;
-
-    for (int i = 0; i < num_sprites; ++i) {
-      int vi = primitive->get_vertex(i);
-
-      nassertv(sd < sd_end && sdp < sdp_end);
-      if (has_color) {
-        color.set_vertex(vi);
-        sd->_color = color.get_data4f();
-      }
-      if (has_rotate) {
-        rotate.set_vertex(vi);
-        sd->_rotate = rotate.get_data1f();
-      }
-      if (has_scale_x) {
-        scale_x.set_vertex(vi);
-        sd->_scale_x = scale_x.get_data1f();
-      } else {
-        sd->_scale_x = 1.0f;
-      }
-      if (has_scale_y) {
-        scale_y.set_vertex(vi);
-        sd->_scale_y = scale_y.get_data1f();
-      } else {
-        sd->_scale_y = 1.0f;
-      }
-
-      // The point in eye-space coordinates.
-      vertex.set_vertex(vi);
-      sd->_eye = modelview.xform_point(vertex.get_data3f());
-      (*sdp) = sd;
-      ++sd;
-      ++sdp;
-    }
-
-    // Now sort the sprites in order from back-to-front so they will
-    // render properly with transparency, at least with each other.
-    ::sort(sprite_pointers, sprite_pointers + num_sprites,
-           IndirectLess<SpriteData>());
-
-    // Point radius, converted from pixels into camera space.
-    float rx = _gl_point_size / _viewport_width;
-    float ry = _gl_point_size / _viewport_height;
-
-    // Go through the sprites, now in sorted order, and render a quad
-    // for each one.
-    GLP(Begin)(GL_QUADS);
-    for (sdp = sprite_pointers; sdp < sdp_end; ++sdp) {
-      sd = (*sdp);
-      if (has_color) {
-        GLP(Color4fv)(sd->_color.get_data());
-      }
-      // The point in eye-space coordinates.
-      const LPoint3f &eye = sd->_eye;
-
-      // The point in camera-space coordinates.
-      LPoint4f p4 = LPoint4f(eye[0], eye[1], eye[2], 1.0f) * _projection_mat;
-      LPoint3f c(p4[0] / p4[3], p4[1] / p4[3], p4[2] / p4[3]);
-
-      // Define the first two corners based on the scales in X and Y.
-      LPoint2f c0(sd->_scale_x, sd->_scale_y);
-      LPoint2f c1(-sd->_scale_x, sd->_scale_y);
-
-      if (has_rotate) {
-        // If we have a rotate factor, apply it to those two corners.
-        LMatrix3f mat = LMatrix3f::rotate_mat(sd->_rotate);
-        c0 = c0 * mat;
-        c1 = c1 * mat;
-      }
-
-      // Now scale the corners to compensate for the aspect ratio of
-      // the viewport (as well as applying the current
-      // RenderModeAttrib's thickness, i.e. point size).
-      c0.set(c0[0] * rx, c0[1] * ry);
-      c1.set(c1[0] * rx, c1[1] * ry);
-
-      if (_point_perspective) {
-        // If _point_perspective is in effect, we should divide the
-        // radius by the distance from the camera plane (which is
-        // -eye[2]), to emulate the glPointParameters() behavior.
-        float scale = (-1.0f / eye[2]);
-        c0 *= scale;
-        c1 *= scale;
-      }
-
-      GLP(TexCoord2f)(1.0f, 0.0f);
-      GLP(Vertex3f)(c[0] + c0[0], c[1] + c0[1], c[2]);
-      GLP(TexCoord2f)(0.0f, 0.0f);
-      GLP(Vertex3f)(c[0] + c1[0], c[1] + c1[1], c[2]);
-      GLP(TexCoord2f)(0.0f, 1.0f);
-      GLP(Vertex3f)(c[0] - c0[0], c[1] - c0[1], c[2]);
-      GLP(TexCoord2f)(1.0f, 1.0f);
-      GLP(Vertex3f)(c[0] - c1[0], c[1] - c1[1], c[2]);
-    }
-    GLP(End)();
-
-    GLP(MatrixMode)(GL_PROJECTION);
-    GLP(PopMatrix)();
-    GLP(MatrixMode)(GL_MODELVIEW);
-    GLP(PopMatrix)();
-
-  } else {
-    // In this case, the sprites are simple squares, so we can take
-    // advantage of the point_sprite extension to draw them quickly.
-    _vertices_other_pcollector.add_level(primitive->get_num_vertices());
-    const unsigned short *client_pointer = setup_primitive(primitive);
-    
-    _glDrawRangeElements(GL_POINTS, 
-                         primitive->get_min_vertex(),
-                         primitive->get_max_vertex(),
-                         primitive->get_num_vertices(),
-                         GL_UNSIGNED_SHORT, client_pointer);
-  }
-  report_my_gl_errors();
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CLP(GraphicsStateGuardian)::end_draw_primitives()
 //     Function: CLP(GraphicsStateGuardian)::end_draw_primitives()
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -2749,6 +2594,14 @@ end_draw_primitives() {
     GLP(LoadMatrixf)(_transform->get_mat().get_data());
     GLP(LoadMatrixf)(_transform->get_mat().get_data());
   }
   }
 
 
+  if (_vertex_data->is_vertex_transformed()) {
+    // Restore the matrices that we pushed above.
+    GLP(MatrixMode)(GL_PROJECTION);
+    GLP(PopMatrix)();
+    GLP(MatrixMode)(GL_MODELVIEW);
+    GLP(PopMatrix)();
+  }
+
   GraphicsStateGuardian::end_draw_primitives();
   GraphicsStateGuardian::end_draw_primitives();
 }
 }
 
 
@@ -3627,6 +3480,7 @@ issue_render_mode(const RenderModeAttrib *attrib) {
   _render_mode = attrib->get_mode();
   _render_mode = attrib->get_mode();
   _point_size = attrib->get_thickness();
   _point_size = attrib->get_thickness();
   _point_perspective = attrib->get_perspective();
   _point_perspective = attrib->get_perspective();
+  _point_perspective = true;
 
 
   switch (_render_mode) {
   switch (_render_mode) {
   case RenderModeAttrib::M_unchanged:
   case RenderModeAttrib::M_unchanged:
@@ -3649,6 +3503,7 @@ issue_render_mode(const RenderModeAttrib *attrib) {
 
 
   // The thickness affects both the line width and the point size.
   // The thickness affects both the line width and the point size.
   GLP(LineWidth)(_point_size);
   GLP(LineWidth)(_point_size);
+  GLP(PointSize)(_point_size);
   report_my_gl_errors();
   report_my_gl_errors();
 
 
   do_point_size();
   do_point_size();
@@ -4055,8 +3910,8 @@ wants_texcoords() const {
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: This function may only be called during a render
 //  Description: This function may only be called during a render
 //               traversal; it will compute the distance to the
 //               traversal; it will compute the distance to the
-//               indicated point, assumed to be in modelview
-//               coordinates, from the camera plane.
+//               indicated point, assumed to be in eye coordinates,
+//               from the camera plane.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float CLP(GraphicsStateGuardian)::
 float CLP(GraphicsStateGuardian)::
 compute_distance_to(const LPoint3f &point) const {
 compute_distance_to(const LPoint3f &point) const {
@@ -5968,6 +5823,10 @@ finish_modify_state() {
           GLP(PopMatrix)();
           GLP(PopMatrix)();
         }
         }
         break;
         break;
+
+      case TexGenAttrib::M_point_sprite:
+        // TODO.
+        break;
       }
       }
     }
     }
 
 
@@ -6276,8 +6135,6 @@ do_point_size() {
   if (!_point_perspective) {
   if (!_point_perspective) {
     // Normal, constant-sized points.  Here _point_size is a width in
     // Normal, constant-sized points.  Here _point_size is a width in
     // pixels.
     // pixels.
-    GLP(PointSize)(_point_size);
-    _gl_point_size = _point_size;
     static LVecBase3f constant(1.0f, 0.0f, 0.0f);
     static LVecBase3f constant(1.0f, 0.0f, 0.0f);
     _glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, constant.get_data());
     _glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, constant.get_data());
 
 
@@ -6286,25 +6143,11 @@ do_point_size() {
     // units.  To arrange that, we need to figure out the appropriate
     // units.  To arrange that, we need to figure out the appropriate
     // scaling factor based on the current viewport and projection
     // scaling factor based on the current viewport and projection
     // matrix.
     // matrix.
-    LVector3f width(_point_size, 0.0f, 1.0f);
-    width = width * _projection_mat;
-    _gl_point_size = width[0] * _viewport_width;
-
-    if (!_supports_point_parameters) {
-      // Actually, this OpenGL driver doesn't support the
-      // glPointParameter() extension.  That means we can't get
-      // perspective correct points anyway.  Maybe we should render
-      // them as sprites?  As a stopgap, we'll just ask for 1-pixel
-      // points.
-      GLP(PointSize)(1.0f);
-
-    } else {
-      // This driver does support the extension, so get the
-      // appropriate width.
-      GLP(PointSize)(width[0] * _viewport_width);
-      static LVecBase3f square(0.0f, 0.0f, 1.0f);
-      _glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, square.get_data());
-    }
+    LVector3f height(0.0f, _point_size, 1.0f);
+    height = height * _projection_mat;
+    float s = height[1] * _viewport_height / _point_size;
+    LVecBase3f square(0.0f, 0.0f, 1.0f / (s * s));
+    _glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, square.get_data());
   }
   }
 
 
   report_my_gl_errors();
   report_my_gl_errors();

+ 0 - 13
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -101,7 +101,6 @@ public:
   virtual void draw_tristrips(const qpGeomTristrips *primitive);
   virtual void draw_tristrips(const qpGeomTristrips *primitive);
   virtual void draw_lines(const qpGeomLines *primitive);
   virtual void draw_lines(const qpGeomLines *primitive);
   virtual void draw_points(const qpGeomPoints *primitive);
   virtual void draw_points(const qpGeomPoints *primitive);
-  virtual void draw_sprites(const qpGeomSprites *primitive);
   virtual void end_draw_primitives();
   virtual void end_draw_primitives();
 
 
   INLINE bool draw_display_list(GeomContext *gc);
   INLINE bool draw_display_list(GeomContext *gc);
@@ -317,7 +316,6 @@ protected:
   bool _auto_antialias_mode;
   bool _auto_antialias_mode;
   RenderModeAttrib::Mode _render_mode;
   RenderModeAttrib::Mode _render_mode;
   float _point_size;
   float _point_size;
-  float _gl_point_size;
   bool _point_perspective;
   bool _point_perspective;
 
 
   bool _transform_stale;
   bool _transform_stale;
@@ -392,17 +390,6 @@ public:
   typedef pvector<GLuint> DeletedDisplayLists;
   typedef pvector<GLuint> DeletedDisplayLists;
   DeletedDisplayLists _deleted_display_lists;
   DeletedDisplayLists _deleted_display_lists;
 
 
-  // This class is used internally by draw_sprites().
-  class SpriteData {
-  public:
-    INLINE bool operator < (const SpriteData &other) const;
-    Vertexf _eye;
-    Colorf _color;
-    float _rotate;
-    float _scale_x;
-    float _scale_y;
-  };
-
 public:
 public:
   static GraphicsStateGuardian *
   static GraphicsStateGuardian *
   make_GlGraphicsStateGuardian(const FactoryParams &params);
   make_GlGraphicsStateGuardian(const FactoryParams &params);

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

@@ -27,7 +27,6 @@
     qpgeomLines.h \
     qpgeomLines.h \
     qpgeomLinestrips.h \
     qpgeomLinestrips.h \
     qpgeomPoints.h \
     qpgeomPoints.h \
-    qpgeomSprites.h \
     qpgeomUsageHint.h \
     qpgeomUsageHint.h \
     qpgeomVertexArrayData.h qpgeomVertexArrayData.I \
     qpgeomVertexArrayData.h qpgeomVertexArrayData.I \
     qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \
     qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \
@@ -80,7 +79,6 @@
     qpgeomLines.cxx \
     qpgeomLines.cxx \
     qpgeomLinestrips.cxx \
     qpgeomLinestrips.cxx \
     qpgeomPoints.cxx \
     qpgeomPoints.cxx \
-    qpgeomSprites.cxx \
     qpgeomVertexArrayData.cxx \
     qpgeomVertexArrayData.cxx \
     qpgeomVertexArrayFormat.cxx \
     qpgeomVertexArrayFormat.cxx \
     qpgeomCacheEntry.cxx \
     qpgeomCacheEntry.cxx \
@@ -130,7 +128,6 @@
     qpgeomLines.h \
     qpgeomLines.h \
     qpgeomLinestrips.h \
     qpgeomLinestrips.h \
     qpgeomPoints.h \
     qpgeomPoints.h \
-    qpgeomSprites.h \
     qpgeomUsageHint.h \
     qpgeomUsageHint.h \
     qpgeomVertexArrayData.h qpgeomVertexArrayData.I \
     qpgeomVertexArrayData.h qpgeomVertexArrayData.I \
     qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \
     qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \

+ 0 - 3
panda/src/gobj/config_gobj.cxx

@@ -31,7 +31,6 @@
 #include "qpgeomLines.h"
 #include "qpgeomLines.h"
 #include "qpgeomLinestrips.h"
 #include "qpgeomLinestrips.h"
 #include "qpgeomPoints.h"
 #include "qpgeomPoints.h"
-#include "qpgeomSprites.h"
 #include "qpgeomVertexArrayData.h"
 #include "qpgeomVertexArrayData.h"
 #include "qpgeomVertexArrayFormat.h"
 #include "qpgeomVertexArrayFormat.h"
 #include "qpgeomVertexData.h"
 #include "qpgeomVertexData.h"
@@ -257,7 +256,6 @@ ConfigureFn(config_gobj) {
   qpGeomLines::init_type();
   qpGeomLines::init_type();
   qpGeomLinestrips::init_type();
   qpGeomLinestrips::init_type();
   qpGeomPoints::init_type();
   qpGeomPoints::init_type();
-  qpGeomSprites::init_type();
   qpGeomVertexArrayData::init_type();
   qpGeomVertexArrayData::init_type();
   qpGeomVertexArrayFormat::init_type();
   qpGeomVertexArrayFormat::init_type();
   qpGeomVertexData::init_type();
   qpGeomVertexData::init_type();
@@ -302,7 +300,6 @@ ConfigureFn(config_gobj) {
   qpGeomLines::register_with_read_factory();
   qpGeomLines::register_with_read_factory();
   qpGeomLinestrips::register_with_read_factory();
   qpGeomLinestrips::register_with_read_factory();
   qpGeomPoints::register_with_read_factory();
   qpGeomPoints::register_with_read_factory();
-  qpGeomSprites::register_with_read_factory();
   qpGeomVertexArrayData::register_with_read_factory();
   qpGeomVertexArrayData::register_with_read_factory();
   qpGeomVertexArrayFormat::register_with_read_factory();
   qpGeomVertexArrayFormat::register_with_read_factory();
   qpGeomVertexData::register_with_read_factory();
   qpGeomVertexData::register_with_read_factory();

+ 0 - 1
panda/src/gobj/gobj_composite1.cxx

@@ -20,7 +20,6 @@
 #include "qpgeomLines.cxx"
 #include "qpgeomLines.cxx"
 #include "qpgeomLinestrips.cxx"
 #include "qpgeomLinestrips.cxx"
 #include "qpgeomPoints.cxx"
 #include "qpgeomPoints.cxx"
-#include "qpgeomSprites.cxx"
 #include "qpgeomVertexArrayData.cxx"
 #include "qpgeomVertexArrayData.cxx"
 #include "qpgeomVertexArrayFormat.cxx"
 #include "qpgeomVertexArrayFormat.cxx"
 #include "qpgeomCacheEntry.cxx"
 #include "qpgeomCacheEntry.cxx"

+ 12 - 12
panda/src/gobj/internalName.I

@@ -192,29 +192,29 @@ get_rotate() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: InternalName::get_scale_x
+//     Function: InternalName::get_size
 //       Access: Published, Static
 //       Access: Published, Static
-//  Description: Returns the standard InternalName "scale_x".
+//  Description: Returns the standard InternalName "size".
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PT(InternalName) InternalName::
 INLINE PT(InternalName) InternalName::
-get_scale_x() {
-  if (_scale_x == (InternalName *)NULL) {
-    _scale_x = InternalName::make("scale_x");
+get_size() {
+  if (_size == (InternalName *)NULL) {
+    _size = InternalName::make("size");
   }
   }
-  return _scale_x;
+  return _size;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: InternalName::get_scale_y
+//     Function: InternalName::get_aspect_ratio
 //       Access: Published, Static
 //       Access: Published, Static
-//  Description: Returns the standard InternalName "scale_y".
+//  Description: Returns the standard InternalName "aspect_ratio".
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PT(InternalName) InternalName::
 INLINE PT(InternalName) InternalName::
-get_scale_y() {
-  if (_scale_y == (InternalName *)NULL) {
-    _scale_y = InternalName::make("scale_y");
+get_aspect_ratio() {
+  if (_aspect_ratio == (InternalName *)NULL) {
+    _aspect_ratio = InternalName::make("aspect_ratio");
   }
   }
-  return _scale_y;
+  return _aspect_ratio;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/gobj/internalName.cxx

@@ -33,8 +33,8 @@ PT(InternalName) InternalName::_binormal;
 PT(InternalName) InternalName::_texcoord;
 PT(InternalName) InternalName::_texcoord;
 PT(InternalName) InternalName::_color;
 PT(InternalName) InternalName::_color;
 PT(InternalName) InternalName::_rotate;
 PT(InternalName) InternalName::_rotate;
-PT(InternalName) InternalName::_scale_x;
-PT(InternalName) InternalName::_scale_y;
+PT(InternalName) InternalName::_size;
+PT(InternalName) InternalName::_aspect_ratio;
 PT(InternalName) InternalName::_transform_blend;
 PT(InternalName) InternalName::_transform_blend;
 PT(InternalName) InternalName::_transform_weight;
 PT(InternalName) InternalName::_transform_weight;
 PT(InternalName) InternalName::_transform_index;
 PT(InternalName) InternalName::_transform_index;

+ 4 - 4
panda/src/gobj/internalName.h

@@ -72,8 +72,8 @@ PUBLISHED:
   INLINE static PT(InternalName) get_texcoord_name(const string &name);
   INLINE static PT(InternalName) get_texcoord_name(const string &name);
   INLINE static PT(InternalName) get_color();
   INLINE static PT(InternalName) get_color();
   INLINE static PT(InternalName) get_rotate();
   INLINE static PT(InternalName) get_rotate();
-  INLINE static PT(InternalName) get_scale_x();
-  INLINE static PT(InternalName) get_scale_y();
+  INLINE static PT(InternalName) get_size();
+  INLINE static PT(InternalName) get_aspect_ratio();
   INLINE static PT(InternalName) get_transform_blend();
   INLINE static PT(InternalName) get_transform_blend();
   INLINE static PT(InternalName) get_transform_weight();
   INLINE static PT(InternalName) get_transform_weight();
   INLINE static PT(InternalName) get_transform_index();
   INLINE static PT(InternalName) get_transform_index();
@@ -96,8 +96,8 @@ private:
   static PT(InternalName) _texcoord;
   static PT(InternalName) _texcoord;
   static PT(InternalName) _color;
   static PT(InternalName) _color;
   static PT(InternalName) _rotate;
   static PT(InternalName) _rotate;
-  static PT(InternalName) _scale_x;
-  static PT(InternalName) _scale_y;
+  static PT(InternalName) _size;
+  static PT(InternalName) _aspect_ratio;
   static PT(InternalName) _transform_blend;
   static PT(InternalName) _transform_blend;
   static PT(InternalName) _transform_weight;
   static PT(InternalName) _transform_weight;
   static PT(InternalName) _transform_index;
   static PT(InternalName) _transform_index;

+ 24 - 2
panda/src/gobj/qpgeom.I

@@ -48,6 +48,19 @@ get_usage_hint() const {
   return cdata->_usage_hint;
   return cdata->_usage_hint;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::get_point_rendering
+//       Access: Published
+//  Description: Returns the set of PointRendering bits that represent
+//               the rendering properties required to properly render
+//               the points (if any) represented within this Geom.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeom::
+get_point_rendering() const {
+  CDReader cdata(_cycler);
+  return cdata->_point_rendering;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::get_vertex_data
 //     Function: qpGeom::get_vertex_data
 //       Access: Published
 //       Access: Published
@@ -129,7 +142,8 @@ get_modified() const {
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE qpGeom::CacheEntry::
 INLINE qpGeom::CacheEntry::
-CacheEntry(const qpGeomMunger *modifier) :
+CacheEntry(const qpGeomVertexData *source_data, const qpGeomMunger *modifier) :
+  _source_data(source_data),
   _modifier(modifier)
   _modifier(modifier)
 {
 {
 }
 }
@@ -142,7 +156,13 @@ CacheEntry(const qpGeomMunger *modifier) :
 INLINE bool qpGeom::CacheEntry::
 INLINE bool qpGeom::CacheEntry::
 operator < (const CacheEntry &other) const {
 operator < (const CacheEntry &other) const {
   if (_modifier != other._modifier) {
   if (_modifier != other._modifier) {
-    return (_modifier->geom_compare_to(*other._modifier) < 0);
+    int compare = _modifier->geom_compare_to(*other._modifier);
+    if (compare != 0) {
+      return (compare < 0);
+    }
+  }
+  if (_source_data != other._source_data) {
+    return (_source_data < other._source_data);
   }
   }
   return 0;
   return 0;
 }
 }
@@ -156,6 +176,7 @@ operator < (const CacheEntry &other) const {
 INLINE qpGeom::CData::
 INLINE qpGeom::CData::
 CData() :
 CData() :
   _primitive_type(qpGeomPrimitive::PT_none),
   _primitive_type(qpGeomPrimitive::PT_none),
+  _point_rendering(0),
   _usage_hint(qpGeomUsageHint::UH_static),
   _usage_hint(qpGeomUsageHint::UH_static),
   _got_usage_hint(false)
   _got_usage_hint(false)
 {
 {
@@ -171,6 +192,7 @@ CData(const qpGeom::CData &copy) :
   _data(copy._data),
   _data(copy._data),
   _primitives(copy._primitives),
   _primitives(copy._primitives),
   _primitive_type(copy._primitive_type),
   _primitive_type(copy._primitive_type),
+  _point_rendering(copy._point_rendering),
   _usage_hint(copy._usage_hint),
   _usage_hint(copy._usage_hint),
   _got_usage_hint(copy._got_usage_hint),
   _got_usage_hint(copy._got_usage_hint),
   _modified(copy._modified)
   _modified(copy._modified)

+ 41 - 6
panda/src/gobj/qpgeom.cxx

@@ -138,6 +138,7 @@ set_vertex_data(const qpGeomVertexData *data) {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
   cdata->_data = (qpGeomVertexData *)data;
   cdata->_data = (qpGeomVertexData *)data;
   mark_bound_stale();
   mark_bound_stale();
+  reset_point_rendering(cdata);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -172,7 +173,11 @@ set_primitive(int i, const qpGeomPrimitive *primitive) {
     }
     }
   }
   }
   cdata->_primitives[i] = (qpGeomPrimitive *)primitive;
   cdata->_primitives[i] = (qpGeomPrimitive *)primitive;
-  cdata->_primitive_type = primitive->get_primitive_type();
+  qpGeomPrimitive::PrimitiveType new_primitive_type = primitive->get_primitive_type();
+  if (new_primitive_type != cdata->_primitive_type) {
+    cdata->_primitive_type = new_primitive_type;
+    reset_point_rendering(cdata);
+  }
   cdata->_modified = qpGeom::get_next_modified();
   cdata->_modified = qpGeom::get_next_modified();
 }
 }
 
 
@@ -197,7 +202,11 @@ add_primitive(const qpGeomPrimitive *primitive) {
            cdata->_primitive_type == primitive->get_primitive_type());
            cdata->_primitive_type == primitive->get_primitive_type());
 
 
   cdata->_primitives.push_back((qpGeomPrimitive *)primitive);
   cdata->_primitives.push_back((qpGeomPrimitive *)primitive);
-  cdata->_primitive_type = primitive->get_primitive_type();
+  qpGeomPrimitive::PrimitiveType new_primitive_type = primitive->get_primitive_type();
+  if (new_primitive_type != cdata->_primitive_type) {
+    cdata->_primitive_type = new_primitive_type;
+    reset_point_rendering(cdata);
+  }
 
 
   if (cdata->_got_usage_hint) {
   if (cdata->_got_usage_hint) {
     cdata->_usage_hint = min(cdata->_usage_hint, primitive->get_usage_hint());
     cdata->_usage_hint = min(cdata->_usage_hint, primitive->get_usage_hint());
@@ -224,6 +233,7 @@ remove_primitive(int i) {
   cdata->_primitives.erase(cdata->_primitives.begin() + i);
   cdata->_primitives.erase(cdata->_primitives.begin() + i);
   if (cdata->_primitives.empty()) {
   if (cdata->_primitives.empty()) {
     cdata->_primitive_type = qpGeomPrimitive::PT_none;
     cdata->_primitive_type = qpGeomPrimitive::PT_none;
+    reset_point_rendering(cdata);
   }
   }
   cdata->_modified = qpGeom::get_next_modified();
   cdata->_modified = qpGeom::get_next_modified();
 }
 }
@@ -242,6 +252,7 @@ clear_primitives() {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
   cdata->_primitives.clear();
   cdata->_primitives.clear();
   cdata->_primitive_type = qpGeomPrimitive::PT_none;
   cdata->_primitive_type = qpGeomPrimitive::PT_none;
+  reset_point_rendering(cdata);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -316,17 +327,19 @@ transform_vertices(const LMatrix4f &mat) {
 void qpGeom::
 void qpGeom::
 munge_geom(const qpGeomMunger *munger,
 munge_geom(const qpGeomMunger *munger,
            CPT(qpGeom) &result, CPT(qpGeomVertexData) &data) const {
            CPT(qpGeom) &result, CPT(qpGeomVertexData) &data) const {
+  CPT(qpGeomVertexData) source_data = data;
+
   // Look up the munger in our cache--maybe we've recently applied it.
   // Look up the munger in our cache--maybe we've recently applied it.
   {
   {
     CDReader cdata(_cycler);
     CDReader cdata(_cycler);
-    CacheEntry temp_entry(munger);
+    CacheEntry temp_entry(source_data, munger);
     temp_entry.ref();  // big ugly hack to allow a stack-allocated ReferenceCount object.
     temp_entry.ref();  // big ugly hack to allow a stack-allocated ReferenceCount object.
     Cache::const_iterator ci = cdata->_cache.find(&temp_entry);
     Cache::const_iterator ci = cdata->_cache.find(&temp_entry);
     if (ci != cdata->_cache.end()) {
     if (ci != cdata->_cache.end()) {
       CacheEntry *entry = (*ci);
       CacheEntry *entry = (*ci);
 
 
       if (get_modified() <= entry->_geom_result->get_modified() &&
       if (get_modified() <= entry->_geom_result->get_modified() &&
-          get_vertex_data()->get_modified() <= entry->_data_result->get_modified()) {
+          data->get_modified() <= entry->_data_result->get_modified()) {
         // The cache entry is still good; use it.
         // The cache entry is still good; use it.
 
 
         // Record a cache hit, so this element will stay in the cache a
         // Record a cache hit, so this element will stay in the cache a
@@ -354,7 +367,6 @@ munge_geom(const qpGeomMunger *munger,
   PStatTimer timer(qpGeomMunger::_munge_pcollector);
   PStatTimer timer(qpGeomMunger::_munge_pcollector);
 
 
   result = this;
   result = this;
-  data = get_vertex_data();
   if (munger != (qpGeomMunger *)NULL) {
   if (munger != (qpGeomMunger *)NULL) {
     data = munger->munge_data(data);
     data = munger->munge_data(data);
     ((qpGeomMunger *)munger)->munge_geom_impl(result, data);
     ((qpGeomMunger *)munger)->munge_geom_impl(result, data);
@@ -365,7 +377,7 @@ munge_geom(const qpGeomMunger *munger,
     CacheEntry *entry;
     CacheEntry *entry;
     {
     {
       CDWriter cdata(((qpGeom *)this)->_cycler);
       CDWriter cdata(((qpGeom *)this)->_cycler);
-      entry = new CacheEntry(munger);
+      entry = new CacheEntry(source_data, munger);
       entry->_source = (qpGeom *)this; 
       entry->_source = (qpGeom *)this; 
       entry->_geom_result = result;
       entry->_geom_result = result;
       entry->_data_result = data;
       entry->_data_result = data;
@@ -630,6 +642,29 @@ reset_usage_hint(qpGeom::CDWriter &cdata) {
   cdata->_got_usage_hint = true;
   cdata->_got_usage_hint = true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::reset_point_rendering
+//       Access: Private
+//  Description: Rederives the _point_rendering member.
+////////////////////////////////////////////////////////////////////
+void qpGeom::
+reset_point_rendering(qpGeom::CDWriter &cdata) {
+  cdata->_point_rendering = 0;
+  if (cdata->_primitive_type == qpGeomPrimitive::PT_points) {
+    cdata->_point_rendering |= PR_point;
+
+    if (cdata->_data->has_column(InternalName::get_size())) {
+      cdata->_point_rendering |= PR_per_point_size;
+    }
+    if (cdata->_data->has_column(InternalName::get_aspect_ratio())) {
+      cdata->_point_rendering |= PR_aspect_ratio;
+    }
+    if (cdata->_data->has_column(InternalName::get_rotate())) {
+      cdata->_point_rendering |= PR_rotate;
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::register_with_read_factory
 //     Function: qpGeom::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 31 - 1
panda/src/gobj/qpgeom.h

@@ -66,8 +66,34 @@ PUBLISHED:
   // Temporary.
   // Temporary.
   virtual Geom *make_copy() const;
   virtual Geom *make_copy() const;
 
 
+  enum PointRendering {
+    // If there are any points at all.
+    PR_point          = 0x0001,
+
+    // If the points are all the same size, other than 1 pixel.
+    PR_uniform_size   = 0x0002,
+
+    // If the points have a per-vertex size designation.
+    PR_per_point_size = 0x0004,
+
+    // If the points' size is specified in camera units rather than
+    // screen pixels.
+    PR_perspective    = 0x0008,
+
+    // If the points have a non-square aspect ratio.
+    PR_aspect_ratio   = 0x0010,
+
+    // If the points are rotated off the orthonormal axis.
+    PR_rotate         = 0x0020,
+
+    // If the points require texture coordinates interpolated across
+    // their face, to render textures as sprites.
+    PR_sprite         = 0x0040,
+  };
+
   INLINE qpGeomPrimitive::PrimitiveType get_primitive_type() const;
   INLINE qpGeomPrimitive::PrimitiveType get_primitive_type() const;
   INLINE qpGeomUsageHint::UsageHint get_usage_hint() const;
   INLINE qpGeomUsageHint::UsageHint get_usage_hint() const;
+  INLINE int get_point_rendering() const;
 
 
   INLINE CPT(qpGeomVertexData) get_vertex_data() const;
   INLINE CPT(qpGeomVertexData) get_vertex_data() const;
   PT(qpGeomVertexData) modify_vertex_data();
   PT(qpGeomVertexData) modify_vertex_data();
@@ -126,7 +152,8 @@ private:
   // to avoid cache bloat.
   // to avoid cache bloat.
   class CacheEntry : public qpGeomCacheEntry {
   class CacheEntry : public qpGeomCacheEntry {
   public:
   public:
-    INLINE CacheEntry(const qpGeomMunger *modifier);
+    INLINE CacheEntry(const qpGeomVertexData *source_data,
+                      const qpGeomMunger *modifier);
     INLINE bool operator < (const CacheEntry &other) const;
     INLINE bool operator < (const CacheEntry &other) const;
 
 
     virtual void evict_callback();
     virtual void evict_callback();
@@ -134,6 +161,7 @@ private:
     virtual void output(ostream &out) const;
     virtual void output(ostream &out) const;
 
 
     qpGeom *_source;
     qpGeom *_source;
+    CPT(qpGeomVertexData) _source_data;
     CPT(qpGeomMunger) _modifier;
     CPT(qpGeomMunger) _modifier;
     CPT(qpGeom) _geom_result;
     CPT(qpGeom) _geom_result;
     CPT(qpGeomVertexData) _data_result;
     CPT(qpGeomVertexData) _data_result;
@@ -153,6 +181,7 @@ private:
     PT(qpGeomVertexData) _data;
     PT(qpGeomVertexData) _data;
     Primitives _primitives;
     Primitives _primitives;
     qpGeomPrimitive::PrimitiveType _primitive_type;
     qpGeomPrimitive::PrimitiveType _primitive_type;
+    int _point_rendering;
     qpGeomUsageHint::UsageHint _usage_hint;
     qpGeomUsageHint::UsageHint _usage_hint;
     bool _got_usage_hint;
     bool _got_usage_hint;
     UpdateSeq _modified;
     UpdateSeq _modified;
@@ -164,6 +193,7 @@ private:
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataWriter<CData> CDWriter;
 
 
   void reset_usage_hint(CDWriter &cdata);
   void reset_usage_hint(CDWriter &cdata);
+  void reset_point_rendering(CDWriter &cdata);
 
 
   static UpdateSeq _next_modified;
   static UpdateSeq _next_modified;
 
 

+ 8 - 5
panda/src/gobj/qpgeomMunger.cxx

@@ -163,10 +163,11 @@ munge_data_impl(const qpGeomVertexData *data) {
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description: Converts a Geom and/or its data as necessary.
 //  Description: Converts a Geom and/or its data as necessary.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void qpGeomMunger::
+bool qpGeomMunger::
 munge_geom_impl(CPT(qpGeom) &, CPT(qpGeomVertexData) &) {
 munge_geom_impl(CPT(qpGeom) &, CPT(qpGeomVertexData) &) {
   // The default implementation does nothing (the work has already
   // The default implementation does nothing (the work has already
   // been done in munge_format_impl() and munge_data_impl()).
   // been done in munge_format_impl() and munge_data_impl()).
+  return true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -185,10 +186,12 @@ compare_to_impl(const qpGeomMunger *other) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomMunger::geom_compare_to_impl
 //     Function: qpGeomMunger::geom_compare_to_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
-//  Description: Called to compare two GeomMungers who are known to be
-//               of the same type, for an apples-to-apples comparison.
-//               This will never be called on two pointers of a
-//               different type.
+//  Description: Compares two GeomMungers, considering only whether
+//               they would produce a different answer to
+//               munge_format(), munge_data(), or munge_geom().  (They
+//               still might be different in other ways, but if they
+//               would produce the same answer, this function consider
+//               them to be the same.)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int qpGeomMunger::
 int qpGeomMunger::
 geom_compare_to_impl(const qpGeomMunger *other) const {
 geom_compare_to_impl(const qpGeomMunger *other) const {

+ 1 - 1
panda/src/gobj/qpgeomMunger.h

@@ -87,7 +87,7 @@ protected:
   virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig,
   virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig,
                                                     const qpGeomVertexAnimationSpec &animation);
                                                     const qpGeomVertexAnimationSpec &animation);
   virtual CPT(qpGeomVertexData) munge_data_impl(const qpGeomVertexData *data);
   virtual CPT(qpGeomVertexData) munge_data_impl(const qpGeomVertexData *data);
-  virtual void munge_geom_impl(CPT(qpGeom) &geom, CPT(qpGeomVertexData) &data);
+  virtual bool munge_geom_impl(CPT(qpGeom) &geom, CPT(qpGeomVertexData) &data);
   virtual int compare_to_impl(const qpGeomMunger *other) const;
   virtual int compare_to_impl(const qpGeomMunger *other) const;
   virtual int geom_compare_to_impl(const qpGeomMunger *other) const;
   virtual int geom_compare_to_impl(const qpGeomMunger *other) const;
 
 

+ 0 - 148
panda/src/gobj/qpgeomSprites.cxx

@@ -1,148 +0,0 @@
-// Filename: qpgeomSprites.cxx
-// Created by:  drose (05Apr05)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "qpgeomSprites.h"
-#include "pStatTimer.h"
-#include "bamReader.h"
-#include "bamWriter.h"
-
-TypeHandle qpGeomSprites::_type_handle;
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::Constructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-qpGeomSprites::
-qpGeomSprites(qpGeomUsageHint::UsageHint usage_hint) :
-  qpGeomPrimitive(usage_hint)
-{
-}
- 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::Copy Constructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-qpGeomSprites::
-qpGeomSprites(const qpGeomSprites &copy) :
-  qpGeomPrimitive(copy)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::Destructor
-//       Access: Published, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-qpGeomSprites::
-~qpGeomSprites() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::make_copy
-//       Access: Public, Virtual
-//  Description: 
-////////////////////////////////////////////////////////////////////
-PT(qpGeomPrimitive) qpGeomSprites::
-make_copy() const {
-  return new qpGeomSprites(*this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::get_primitive_type
-//       Access: Public, Virtual
-//  Description: Returns the fundamental rendering type of this
-//               primitive: whether it is sprites, lines, or polygons.
-//               This is used primarily to set up the appropriate
-//               antialiasing settings when AntialiasAttrib::M_auto is
-//               in effect.
-////////////////////////////////////////////////////////////////////
-qpGeomPrimitive::PrimitiveType qpGeomSprites::
-get_primitive_type() const {
-  return PT_polygons;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::get_num_vertices_per_primitive
-//       Access: Public, Virtual
-//  Description: If the primitive type is a simple type in which all
-//               primitives have the same number of vertices, like
-//               sprites, returns the number of vertices per
-//               primitive.  If the primitive type is a more complex
-//               type in which different primitives might have
-//               different numbers of vertices, for instance a
-//               sprite strip, returns 0.
-////////////////////////////////////////////////////////////////////
-int qpGeomSprites::
-get_num_vertices_per_primitive() const {
-  return 1;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::get_min_num_vertices_per_primitive
-//       Access: Public, Virtual
-//  Description: Returns the minimum number of vertices that must be
-//               added before close_primitive() may legally be called.
-////////////////////////////////////////////////////////////////////
-int qpGeomSprites::
-get_min_num_vertices_per_primitive() const {
-  return 1;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::draw
-//       Access: Public, Virtual
-//  Description: Calls the appropriate method on the GSG to draw the
-//               primitive.
-////////////////////////////////////////////////////////////////////
-void qpGeomSprites::
-draw(GraphicsStateGuardianBase *gsg) const {
-  gsg->draw_sprites(this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::register_with_read_factory
-//       Access: Public, Static
-//  Description: Tells the BamReader how to create objects of type
-//               qpGeom.
-////////////////////////////////////////////////////////////////////
-void qpGeomSprites::
-register_with_read_factory() {
-  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomSprites::make_from_bam
-//       Access: Protected, Static
-//  Description: This function is called by the BamReader's factory
-//               when a new object of type qpGeom is encountered
-//               in the Bam file.  It should create the qpGeom
-//               and extract its information from the file.
-////////////////////////////////////////////////////////////////////
-TypedWritable *qpGeomSprites::
-make_from_bam(const FactoryParams &params) {
-  qpGeomSprites *object = new qpGeomSprites(qpGeomUsageHint::UH_client);
-  DatagramIterator scan;
-  BamReader *manager;
-
-  parse_params(params, scan, manager);
-  object->fillin(scan, manager);
-
-  return object;
-}

+ 0 - 90
panda/src/gobj/qpgeomSprites.h

@@ -1,90 +0,0 @@
-// Filename: qpgeomSprites.h
-// Created by:  drose (05Apr05)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#ifndef qpGEOMSPRITES_H
-#define qpGEOMSPRITES_H
-
-#include "pandabase.h"
-#include "qpgeomPrimitive.h"
-
-////////////////////////////////////////////////////////////////////
-//       Class : qpGeomSprites
-// Description : Defines a collection of rectangular polygons with the
-//               same texture applied to all of them.  This is most
-//               useful for particle systems.
-//
-//               Each sprite is defined by a single vertex, at the
-//               center of the polygon.  Each polygon may optionally
-//               be rotated by a certain angle, and/or scaled in x and
-//               y; if needed, these parameters are specified
-//               per-vertex with the optional "rotate", "scale_x", and
-//               "scale_y" vertex data column names.
-//
-//               The overall scale of the sprites is controlled by the
-//               current RenderModeAttrib::get_thickness() and
-//               get_perspective() parameters.  If present, scale_x
-//               and scale_y apply an additional per-sprite scale.
-//               The UV range is always (0, 0) in the upper left
-//               corner to (1, 1) in the lower right; use a
-//               TexMatrixAttrib to adjust this.
-//
-//               This is part of the experimental Geom rewrite.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA qpGeomSprites : public qpGeomPrimitive {
-PUBLISHED:
-  qpGeomSprites(qpGeomUsageHint::UsageHint usage_hint);
-  qpGeomSprites(const qpGeomSprites &copy);
-  virtual ~qpGeomSprites();
-
-public:
-  virtual PT(qpGeomPrimitive) make_copy() const;
-  virtual PrimitiveType get_primitive_type() const;
-
-  virtual int get_num_vertices_per_primitive() const;
-  virtual int get_min_num_vertices_per_primitive() const;
-
-public:
-  virtual void draw(GraphicsStateGuardianBase *gsg) const;
-
-public:
-  static void register_with_read_factory();
-
-protected:
-  static TypedWritable *make_from_bam(const FactoryParams &params);
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    qpGeomPrimitive::init_type();
-    register_type(_type_handle, "qpGeomSprites",
-                  qpGeomPrimitive::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;
-
-  friend class qpGeom;
-};
-
-#endif

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

@@ -45,6 +45,7 @@ PUBLISHED:
   enum Contents {
   enum Contents {
     C_other,        // Arbitrary meaning, leave it alone
     C_other,        // Arbitrary meaning, leave it alone
     C_point,        // A point in 3-space or 4-space
     C_point,        // A point in 3-space or 4-space
+    C_clip_point,   // A point pre-transformed into clip coordinates
     C_vector,       // A surface normal, tangent, or binormal
     C_vector,       // A surface normal, tangent, or binormal
     C_texcoord,     // A texture coordinate
     C_texcoord,     // A texture coordinate
     C_color,        // 3- or 4-component color, ordered R, G, B, [A]
     C_color,        // 3- or 4-component color, ordered R, G, B, [A]

+ 51 - 0
panda/src/gobj/qpgeomVertexData.I

@@ -285,6 +285,57 @@ animate_vertices_cull() const {
   return do_animate_vertices(false);
   return do_animate_vertices(false);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::has_vertex
+//       Access: Public
+//  Description: Returns true if the data has a "vertex" column, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexData::
+has_vertex() const {
+  return (_format->get_vertex_column() != (qpGeomVertexColumn *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::is_vertex_transformed
+//       Access: Public
+//  Description: Returns true if the data has a "vertex" column and it
+//               is indicated as having been transformed into clip
+//               coordinates, false if there is no vertex column or if
+//               it contains ordinary 3-d pre-transformation points.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexData::
+is_vertex_transformed() const {
+  const qpGeomVertexColumn *column = _format->get_vertex_column();
+  if (column != (qpGeomVertexColumn *)NULL) {
+    return column->get_contents() == qpGeomVertexColumn::C_clip_point;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::has_normal
+//       Access: Public
+//  Description: Returns true if the data has a "normal" column, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexData::
+has_normal() const {
+  return (_format->get_normal_column() != (qpGeomVertexColumn *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::has_color
+//       Access: Public
+//  Description: Returns true if the data has a "color" column, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool qpGeomVertexData::
+has_color() const {
+  return (_format->get_color_column() != (qpGeomVertexColumn *)NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::pack_abcd
 //     Function: qpGeomVertexData::pack_abcd
 //       Access: Public, Static
 //       Access: Public, Static

+ 78 - 0
panda/src/gobj/qpgeomVertexData.cxx

@@ -885,6 +885,84 @@ get_array_info(const InternalName *name,
   return false;
   return false;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::get_vertex_info
+//       Access: Public
+//  Description: A shortcut to get_array_info() for the "vertex"
+//               column.
+////////////////////////////////////////////////////////////////////
+bool qpGeomVertexData::
+get_vertex_info(const qpGeomVertexArrayData *&array_data,
+                int &num_values, 
+                qpGeomVertexColumn::NumericType &numeric_type, 
+                int &start, int &stride) const {
+  int array_index = _format->get_vertex_array_index();
+  if (array_index >= 0) {
+    const qpGeomVertexColumn *column = _format->get_vertex_column();
+
+    CDReader cdata(_cycler);
+    array_data = cdata->_arrays[array_index];
+    num_values = column->get_num_values();
+    numeric_type = column->get_numeric_type();
+    start = column->get_start();
+    stride = _format->get_array(array_index)->get_stride();
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::get_normal_info
+//       Access: Public
+//  Description: A shortcut to get_array_info() for the "normal"
+//               column.  Note that there is no num_values return,
+//               since normals should always have three values.
+////////////////////////////////////////////////////////////////////
+bool qpGeomVertexData::
+get_normal_info(const qpGeomVertexArrayData *&array_data,
+                qpGeomVertexColumn::NumericType &numeric_type, 
+                int &start, int &stride) const {
+  int array_index = _format->get_normal_array_index();
+  if (array_index >= 0) {
+    const qpGeomVertexColumn *column = _format->get_normal_column();
+    nassertr(column->get_num_values() == 3, false);
+
+    CDReader cdata(_cycler);
+    array_data = cdata->_arrays[array_index];
+    numeric_type = column->get_numeric_type();
+    start = column->get_start();
+    stride = _format->get_array(array_index)->get_stride();
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::get_color_info
+//       Access: Public
+//  Description: A shortcut to get_array_info() for the "color"
+//               column.
+////////////////////////////////////////////////////////////////////
+bool qpGeomVertexData::
+get_color_info(const qpGeomVertexArrayData *&array_data,
+                int &num_values, 
+                qpGeomVertexColumn::NumericType &numeric_type, 
+                int &start, int &stride) const {
+  int array_index = _format->get_color_array_index();
+  if (array_index >= 0) {
+    const qpGeomVertexColumn *column = _format->get_color_column();
+
+    CDReader cdata(_cycler);
+    array_data = cdata->_arrays[array_index];
+    num_values = column->get_num_values();
+    numeric_type = column->get_numeric_type();
+    start = column->get_start();
+    stride = _format->get_array(array_index)->get_stride();
+    return true;
+  }
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::do_animate_vertices
 //     Function: qpGeomVertexData::do_animate_vertices
 //       Access: Private
 //       Access: Private

+ 18 - 0
panda/src/gobj/qpgeomVertexData.h

@@ -154,6 +154,24 @@ public:
                       qpGeomVertexColumn::NumericType &numeric_type, 
                       qpGeomVertexColumn::NumericType &numeric_type, 
                       int &start, int &stride) const;
                       int &start, int &stride) const;
 
 
+  INLINE bool has_vertex() const;
+  INLINE bool is_vertex_transformed() const;
+  bool get_vertex_info(const qpGeomVertexArrayData *&array_data,
+                       int &num_values,
+                       qpGeomVertexColumn::NumericType &numeric_type, 
+                       int &start, int &stride) const;
+
+  INLINE bool has_normal() const;
+  bool get_normal_info(const qpGeomVertexArrayData *&array_data,
+                       qpGeomVertexColumn::NumericType &numeric_type, 
+                       int &start, int &stride) const;
+
+  INLINE bool has_color() const;
+  bool get_color_info(const qpGeomVertexArrayData *&array_data,
+                      int &num_values,
+                      qpGeomVertexColumn::NumericType &numeric_type, 
+                      int &start, int &stride) const;
+
   static INLINE PN_uint32 pack_abcd(unsigned int a, unsigned int b,
   static INLINE PN_uint32 pack_abcd(unsigned int a, unsigned int b,
                                     unsigned int c, unsigned int d);
                                     unsigned int c, unsigned int d);
   static INLINE unsigned int unpack_abcd_a(PN_uint32 data);
   static INLINE unsigned int unpack_abcd_a(PN_uint32 data);

+ 90 - 0
panda/src/gobj/qpgeomVertexFormat.I

@@ -414,6 +414,96 @@ get_v3n3c4t2() {
   return get_registry()->_v3n3c4t2;
   return get_registry()->_v3n3c4t2;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_vertex_array_index
+//       Access: Public
+//  Description: Returns the array index of the array including the
+//               "vertex" column, or -1 if there is no such array.
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexFormat::
+get_vertex_array_index() const {
+  nassertr(_is_registered, -1);
+  return _vertex_array_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_vertex_column
+//       Access: Public
+//  Description: Returns the column definition of the "vertex" column,
+//               or NULL if there is no such column.
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE const qpGeomVertexColumn *qpGeomVertexFormat::
+get_vertex_column() const {
+  nassertr(_is_registered, NULL);
+  return _vertex_column;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_normal_array_index
+//       Access: Public
+//  Description: Returns the array index of the array including the
+//               "normal" column, or -1 if there is no such array.
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexFormat::
+get_normal_array_index() const {
+  nassertr(_is_registered, -1);
+  return _normal_array_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_normal_column
+//       Access: Public
+//  Description: Returns the column definition of the "normal" column,
+//               or NULL if there is no such column.
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE const qpGeomVertexColumn *qpGeomVertexFormat::
+get_normal_column() const {
+  nassertr(_is_registered, NULL);
+  return _normal_column;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_color_array_index
+//       Access: Public
+//  Description: Returns the array index of the array including the
+//               "color" column, or -1 if there is no such array.
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexFormat::
+get_color_array_index() const {
+  nassertr(_is_registered, -1);
+  return _color_array_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexFormat::get_color_column
+//       Access: Public
+//  Description: Returns the column definition of the "color" column,
+//               or NULL if there is no such column.
+//
+//               This may only be called after the format has been
+//               registered.
+////////////////////////////////////////////////////////////////////
+INLINE const qpGeomVertexColumn *qpGeomVertexFormat::
+get_color_column() const {
+  nassertr(_is_registered, NULL);
+  return _color_column;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexFormat::get_registry
 //     Function: qpGeomVertexFormat::get_registry
 //       Access: Private
 //       Access: Private

+ 14 - 1
panda/src/gobj/qpgeomVertexFormat.cxx

@@ -428,7 +428,9 @@ write_with_data(ostream &out, int indent_level,
 //  Description: Quickly looks up the indicated column within all
 //  Description: Quickly looks up the indicated column within all
 //               of the nested arrays and sets array_index and
 //               of the nested arrays and sets array_index and
 //               column appropriately.  Returns true if the data
 //               column appropriately.  Returns true if the data
-//               type exists in this format, false if it does not.
+//               type exists in this format, false if it does not.  If
+//               it returns false, array_index is set to -1, and
+//               column is set to NULL.
 //
 //
 //               This may only be called after the format has been
 //               This may only be called after the format has been
 //               registered.
 //               registered.
@@ -445,6 +447,10 @@ get_array_info(const InternalName *name, int &array_index,
     column = _arrays[array_index]->get_column((*ai).second._column_index);
     column = _arrays[array_index]->get_column((*ai).second._column_index);
     return true;
     return true;
   }
   }
+
+  array_index = -1;
+  column = NULL;
+
   return false;
   return false;
 }
 }
 
 
@@ -576,6 +582,13 @@ do_register() {
 
 
 
 
   _is_registered = true;
   _is_registered = true;
+
+  get_array_info(InternalName::get_vertex(), _vertex_array_index,
+                 _vertex_column);
+  get_array_info(InternalName::get_normal(), _normal_array_index,
+                 _normal_column);
+  get_array_info(InternalName::get_color(), _color_array_index,
+                 _color_column);
 }
 }
  
  
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 14 - 0
panda/src/gobj/qpgeomVertexFormat.h

@@ -147,6 +147,13 @@ public:
                       int &array_index,
                       int &array_index,
                       const qpGeomVertexColumn *&column) const;
                       const qpGeomVertexColumn *&column) const;
 
 
+  INLINE int get_vertex_array_index() const;
+  INLINE const qpGeomVertexColumn *get_vertex_column() const;
+  INLINE int get_normal_array_index() const;
+  INLINE const qpGeomVertexColumn *get_normal_column() const;
+  INLINE int get_color_array_index() const;
+  INLINE const qpGeomVertexColumn *get_color_column() const;
+
   int compare_to(const qpGeomVertexFormat &other) const;
   int compare_to(const qpGeomVertexFormat &other) const;
 
 
 private:
 private:
@@ -173,6 +180,13 @@ private:
   typedef pmap<const InternalName *, DataTypeRecord> DataTypesByName;
   typedef pmap<const InternalName *, DataTypeRecord> DataTypesByName;
   DataTypesByName _columns_by_name;
   DataTypesByName _columns_by_name;
 
 
+  int _vertex_array_index;
+  const qpGeomVertexColumn *_vertex_column;
+  int _normal_array_index;
+  const qpGeomVertexColumn *_normal_column;
+  int _color_array_index;
+  const qpGeomVertexColumn *_color_column;
+
   typedef pvector< CPT(InternalName) > Columns;
   typedef pvector< CPT(InternalName) > Columns;
   Columns _points;
   Columns _points;
   Columns _vectors;
   Columns _vectors;

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

@@ -55,7 +55,6 @@ class qpGeomTrifans;
 class qpGeomLines;
 class qpGeomLines;
 class qpGeomLinestrips;
 class qpGeomLinestrips;
 class qpGeomPoints;
 class qpGeomPoints;
-class qpGeomSprites;
 class qpGeomMunger;
 class qpGeomMunger;
 
 
 class PreparedGraphicsObjects;
 class PreparedGraphicsObjects;
@@ -121,6 +120,7 @@ class Lens;
 class EXPCL_PANDA GraphicsStateGuardianBase : public TypedWritableReferenceCount {
 class EXPCL_PANDA GraphicsStateGuardianBase : public TypedWritableReferenceCount {
 public:
 public:
   virtual bool get_supports_multisample() const=0;
   virtual bool get_supports_multisample() const=0;
+  virtual int get_supported_point_rendering() const=0;
 
 
   // These functions will be queried by the GeomIssuer to determine if
   // These functions will be queried by the GeomIssuer to determine if
   // it should issue normals, texcoords, and/or colors, based on the
   // it should issue normals, texcoords, and/or colors, based on the
@@ -154,9 +154,9 @@ public:
 
 
   // This function may only be called during a render traversal; it
   // This function may only be called during a render traversal; it
   // will compute the distance to the indicated point, assumed to be
   // will compute the distance to the indicated point, assumed to be
-  // in modelview coordinates, from the camera plane.  This is a
-  // virtual function because different GSG's may define the modelview
-  // coordinate space differently.
+  // in eye coordinates, from the camera plane.  This is a virtual
+  // function because different GSG's may define the eye coordinate
+  // space differently.
   virtual float compute_distance_to(const LPoint3f &point) const=0;
   virtual float compute_distance_to(const LPoint3f &point) const=0;
 
 
   // These are used to implement decals.  If depth_offset_decals()
   // These are used to implement decals.  If depth_offset_decals()
@@ -197,7 +197,6 @@ public:
   virtual void draw_lines(const qpGeomLines *primitive)=0;
   virtual void draw_lines(const qpGeomLines *primitive)=0;
   virtual void draw_linestrips(const qpGeomLinestrips *primitive)=0;
   virtual void draw_linestrips(const qpGeomLinestrips *primitive)=0;
   virtual void draw_points(const qpGeomPoints *primitive)=0;
   virtual void draw_points(const qpGeomPoints *primitive)=0;
-  virtual void draw_sprites(const qpGeomSprites *primitive)=0;
   virtual void end_draw_primitives()=0;
   virtual void end_draw_primitives()=0;
 
 
   virtual void framebuffer_copy_to_texture
   virtual void framebuffer_copy_to_texture

+ 4 - 4
panda/src/parametrics/ropeNode.cxx

@@ -346,7 +346,7 @@ render_thread(CullTraverser *trav, CullTraverserData &data,
   
   
   CullableObject *object = new CullableObject(geom, state,
   CullableObject *object = new CullableObject(geom, state,
                                               data._render_transform);
                                               data._render_transform);
-  trav->get_cull_handler()->record_object(object);
+  trav->get_cull_handler()->record_object(object, trav);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -408,7 +408,7 @@ render_tape(CullTraverser *trav, CullTraverserData &data,
   
   
   CullableObject *object = new CullableObject(geom, data._state,
   CullableObject *object = new CullableObject(geom, data._state,
                                               data._render_transform);
                                               data._render_transform);
-  trav->get_cull_handler()->record_object(object);
+  trav->get_cull_handler()->record_object(object, trav);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -477,7 +477,7 @@ render_billboard(CullTraverser *trav, CullTraverserData &data,
   
   
   CullableObject *object = new CullableObject(geom, data._state,
   CullableObject *object = new CullableObject(geom, data._state,
                                               data._render_transform);
                                               data._render_transform);
-  trav->get_cull_handler()->record_object(object);
+  trav->get_cull_handler()->record_object(object, trav);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -557,7 +557,7 @@ render_tube(CullTraverser *trav, CullTraverserData &data,
 
 
   CullableObject *object = new CullableObject(geom, data._state,
   CullableObject *object = new CullableObject(geom, data._state,
                                               data._render_transform);
                                               data._render_transform);
-  trav->get_cull_handler()->record_object(object);
+  trav->get_cull_handler()->record_object(object, trav);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/parametrics/sheetNode.cxx

@@ -334,7 +334,7 @@ render_sheet(CullTraverser *trav, CullTraverserData &data,
 
 
     CullableObject *object = new CullableObject(geom, data._state,
     CullableObject *object = new CullableObject(geom, data._state,
                                                 data._render_transform);
                                                 data._render_transform);
-    trav->get_cull_handler()->record_object(object);
+    trav->get_cull_handler()->record_object(object, trav);
 
 
   } else {
   } else {
     PTA_Vertexf verts;
     PTA_Vertexf verts;
@@ -404,7 +404,7 @@ render_sheet(CullTraverser *trav, CullTraverserData &data,
   
   
     CullableObject *object = new CullableObject(geom, data._state,
     CullableObject *object = new CullableObject(geom, data._state,
                                                 data._render_transform);
                                                 data._render_transform);
-    trav->get_cull_handler()->record_object(object);
+    trav->get_cull_handler()->record_object(object, trav);
   }
   }
 }
 }
 
 

+ 2 - 1
panda/src/particlesystem/ringEmitter.cxx

@@ -25,7 +25,8 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 RingEmitter::
 RingEmitter::
 RingEmitter() :
 RingEmitter() :
-  _radius(1.0f), _aoe(0.0f), _radius_spread(0.0f) {
+  _radius(1.0f), _radius_spread(0.0f), _aoe(0.0f)
+{
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 45 - 3
panda/src/particlesystem/spriteParticleRenderer.I

@@ -29,16 +29,26 @@ get_source_type() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-// Function : SpriteParticleRenderer::set_texture
-//   Access : public
+//    Function : SpriteParticleRenderer::set_texture
+//      Access : Published
+// Description : Sets the renderer up to render the entire texture
+//               image.  The scale of each particle is based on the
+//               size of the texture in each dimension, modified by
+//               texels_per_unit.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void SpriteParticleRenderer::
 INLINE void SpriteParticleRenderer::
-set_texture(Texture *tex) {
+set_texture(Texture *tex, float texels_per_unit) {
   _texture = tex;
   _texture = tex;
   set_ll_uv(TexCoordf(0.0f, 0.0f));
   set_ll_uv(TexCoordf(0.0f, 0.0f));
   set_ur_uv(TexCoordf(1.0f, 1.0f));
   set_ur_uv(TexCoordf(1.0f, 1.0f));
   _source_type = ST_texture;
   _source_type = ST_texture;
 
 
+  // We scale the particle size by the size of the texture.
+  if (_texture != (Texture *)NULL) {
+    set_size(_texture->get_x_size() / texels_per_unit,
+             _texture->get_y_size() / texels_per_unit);
+  }
+
   init_geoms();
   init_geoms();
 }
 }
 
 
@@ -68,6 +78,18 @@ set_ur_uv(const TexCoordf &ur_uv) {
   _ur_uv = ur_uv;
   _ur_uv = ur_uv;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//    Function : SpriteParticleRenderer::set_size
+//      Access : public
+// Description : Sets the size of each particle in world units.
+////////////////////////////////////////////////////////////////////
+INLINE void SpriteParticleRenderer::
+set_size(float width, float height) {
+  _width = width;
+  _height = height;
+  init_geoms();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 // Function : SpriteParticleRenderer::set_color
 // Function : SpriteParticleRenderer::set_color
 //   Access : public
 //   Access : public
@@ -222,6 +244,26 @@ get_ur_uv() const {
   return _ur_uv;
   return _ur_uv;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//    Function : SpriteParticleRenderer::get_width
+//      Access : public
+// Description : Returns the width of each particle in world units.
+////////////////////////////////////////////////////////////////////
+INLINE float SpriteParticleRenderer::
+get_width() const {
+  return _width;
+}
+
+////////////////////////////////////////////////////////////////////
+//    Function : SpriteParticleRenderer::get_height
+//      Access : public
+// Description : Returns the height of each particle in world units.
+////////////////////////////////////////////////////////////////////
+INLINE float SpriteParticleRenderer::
+get_height() const {
+  return _height;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 // Function : SpriteParticleRenderer::get_color
 // Function : SpriteParticleRenderer::get_color
 //   Access : public
 //   Access : public

+ 157 - 87
panda/src/particlesystem/spriteParticleRenderer.cxx

@@ -23,9 +23,11 @@
 #include "dcast.h"
 #include "dcast.h"
 #include "geomSprite.h"
 #include "geomSprite.h"
 #include "qpgeom.h"
 #include "qpgeom.h"
+#include "qpgeomVertexReader.h"
 #include "qpgeomVertexWriter.h"
 #include "qpgeomVertexWriter.h"
 #include "renderModeAttrib.h"
 #include "renderModeAttrib.h"
 #include "texMatrixAttrib.h"
 #include "texMatrixAttrib.h"
+#include "texGenAttrib.h"
 #include "textureAttrib.h"
 #include "textureAttrib.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -106,9 +108,14 @@ make_copy() {
 //               referenced by the indicated NodePath.  This should be
 //               referenced by the indicated NodePath.  This should be
 //               a reference to a GeomNode; it extracts out the
 //               a reference to a GeomNode; it extracts out the
 //               Texture and UV range from the GeomNode.
 //               Texture and UV range from the GeomNode.
+//
+//               If size_from_texels is true, the particle size is
+//               based on the number of texels in the source image;
+//               otherwise, it is based on the size of the polygon
+//               found in the GeomNode.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void SpriteParticleRenderer::
 void SpriteParticleRenderer::
-set_from_node(const NodePath &node_path) {
+set_from_node(const NodePath &node_path, bool size_from_texels) {
   nassertv(!node_path.is_empty());
   nassertv(!node_path.is_empty());
 
 
   // The bottom node must be a GeomNode.  If it is not, find the first
   // The bottom node must be a GeomNode.  If it is not, find the first
@@ -137,44 +144,114 @@ set_from_node(const NodePath &node_path) {
   nassertv(gnode->get_num_geoms() > 0);
   nassertv(gnode->get_num_geoms() > 0);
   const Geom *geom = gnode->get_geom(0);
   const Geom *geom = gnode->get_geom(0);
 
 
-  PTA_TexCoordf texcoords;
-  GeomBindType bind;
-  PTA_ushort tindex;
-  geom->get_texcoords(texcoords, bind, tindex);
-  if (bind != G_PER_VERTEX) {
-    particlesystem_cat.error()
-      << geom_node_path << " has no UV's in its first Geom.\n";
-    return;
-  }
-
-  int num_verts = geom->get_num_vertices();
-  if (num_verts == 0) {
-    particlesystem_cat.error()
-      << geom_node_path << " has no vertices in its first Geom.\n";
-    return;
-  }
-
-  Geom::TexCoordIterator ti = geom->make_texcoord_iterator();
-
-  const TexCoordf &first_texcoord = geom->get_next_texcoord(ti);
-  TexCoordf min_uv = first_texcoord;
-  TexCoordf max_uv = first_texcoord;
-
-  for (int v = 1; v < num_verts; v++) {
-    const TexCoordf &texcoord = geom->get_next_texcoord(ti);    
+  TexCoordf min_uv, max_uv;
+  Vertexf min_xyz, max_xyz;
+
+  if (geom->is_of_type(qpGeom::get_class_type())) {
+    const qpGeom *qpgeom = DCAST(qpGeom, geom);
+    qpGeomVertexReader texcoord(qpgeom->get_vertex_data());
+    qpGeomVertexReader vertex(qpgeom->get_vertex_data());
+
+    bool found_any = false;
+    for (int pi = 0; pi < qpgeom->get_num_primitives(); ++pi) {
+      const qpGeomPrimitive *primitive = qpgeom->get_primitive(pi);
+      for (int vi = 0; vi < primitive->get_num_vertices(); ++vi) {
+        int vert = primitive->get_vertex(vi);
+        texcoord.set_vertex(vert);
+        vertex.set_vertex(vert);
+
+        if (!found_any) {
+          min_uv = max_uv = texcoord.get_data2f();
+          min_xyz = max_xyz = vertex.get_data3f();
+
+        } else {
+          const LVecBase2f &uv = texcoord.get_data2f();
+          const LVecBase3f &xyz = vertex.get_data3f();
+
+          min_uv[0] = min(min_uv[0], uv[0]);
+          max_uv[0] = max(max_uv[0], uv[0]);
+          min_uv[1] = min(min_uv[1], uv[1]);
+          max_uv[1] = max(max_uv[1], uv[1]);
+
+          min_xyz[0] = min(min_xyz[0], xyz[0]);
+          max_xyz[0] = max(max_xyz[0], xyz[0]);
+          min_xyz[1] = min(min_xyz[1], xyz[1]);
+          max_xyz[1] = max(max_xyz[1], xyz[1]);
+          min_xyz[2] = min(min_xyz[2], xyz[2]);
+          max_xyz[2] = max(max_xyz[2], xyz[2]);
+        }
+      }
+    }
 
 
-    min_uv[0] = min(min_uv[0], texcoord[0]);
-    max_uv[0] = max(max_uv[0], texcoord[0]);
-    min_uv[1] = min(min_uv[1], texcoord[1]);
-    max_uv[1] = max(max_uv[1], texcoord[1]);
+  } else {
+    PTA_TexCoordf texcoords;
+    GeomBindType bind;
+    PTA_ushort tindex;
+    geom->get_texcoords(texcoords, bind, tindex);
+    if (bind != G_PER_VERTEX) {
+      particlesystem_cat.error()
+        << geom_node_path << " has no UV's in its first Geom.\n";
+      return;
+    }
+    
+    int num_verts = geom->get_num_vertices();
+    if (num_verts == 0) {
+      particlesystem_cat.error()
+        << geom_node_path << " has no vertices in its first Geom.\n";
+      return;
+    }
+    
+    Geom::TexCoordIterator ti = geom->make_texcoord_iterator();
+    Geom::VertexIterator vi = geom->make_vertex_iterator();
+    
+    const TexCoordf &first_texcoord = geom->get_next_texcoord(ti);
+    const Vertexf &first_vertex = geom->get_next_vertex(vi);
+    min_uv = first_texcoord;
+    max_uv = first_texcoord;
+    min_xyz = first_vertex;
+    max_xyz = first_vertex;
+    
+    for (int v = 1; v < num_verts; v++) {
+      const TexCoordf &texcoord = geom->get_next_texcoord(ti);    
+      const Vertexf &vertex = geom->get_next_vertex(vi);    
+      
+      min_uv[0] = min(min_uv[0], texcoord[0]);
+      max_uv[0] = max(max_uv[0], texcoord[0]);
+      min_uv[1] = min(min_uv[1], texcoord[1]);
+      max_uv[1] = max(max_uv[1], texcoord[1]);
+
+      min_xyz[0] = min(min_xyz[0], vertex[0]);
+      max_xyz[0] = max(max_xyz[0], vertex[0]);
+      min_xyz[1] = min(min_xyz[1], vertex[1]);
+      max_xyz[1] = max(max_xyz[1], vertex[1]);
+      min_xyz[2] = min(min_xyz[2], vertex[2]);
+      max_xyz[2] = max(max_xyz[2], vertex[2]);
+    }
   }
   }
 
 
   // We don't really pay attention to orientation of UV's here; a
   // We don't really pay attention to orientation of UV's here; a
   // minor flaw.  We assume the minimum is in the lower-left, and the
   // minor flaw.  We assume the minimum is in the lower-left, and the
   // maximum is in the upper-right.
   // maximum is in the upper-right.
-  set_texture(tex);
+  _texture = tex;
   set_ll_uv(min_uv);
   set_ll_uv(min_uv);
   set_ur_uv(max_uv);
   set_ur_uv(max_uv);
+
+  float width = max_xyz[0] - min_xyz[0];
+  float height = max(max_xyz[1] - min_xyz[1],
+                     max_xyz[2] - min_xyz[2]);
+
+  if (size_from_texels) {
+    // If size_from_texels is true, we get the particle size from the
+    // number of texels in the source image.
+    float y_texels = _texture->get_y_size() * fabs(_ur_uv[1] - _ll_uv[1]);
+    set_size(y_texels * width / height, y_texels);
+
+  } else {
+    // If size_from_texels is false, we get the particle size from
+    // the size of the polygon.
+    set_size(width, height);
+  }
+
   _source_type = ST_from_node;
   _source_type = ST_from_node;
 
 
   init_geoms();
   init_geoms();
@@ -252,29 +329,23 @@ init_geoms() {
          qpGeomVertexColumn::C_other);
          qpGeomVertexColumn::C_other);
     }
     }
 
 
-    _base_x_scale = _initial_x_scale;
-    _base_y_scale = _base_x_scale;
+    _base_y_scale = _initial_y_scale;
+    _aspect_ratio = _width / _height;
 
 
-    // We also scale the particle size by the number of texels used,
-    // by established convention.
-    float texel_scale = 1.0f;
-    if (_texture != (Texture *)NULL) {
-      float x_texels = _texture->get_x_size() * fabs(_ur_uv[0] - _ll_uv[0]);
-      float y_texels = _texture->get_y_size() * fabs(_ur_uv[1] - _ll_uv[1]);
-      texel_scale = x_texels;
-      _base_y_scale = _base_x_scale / y_texels * x_texels;
-    }
+    float final_x_scale = _animate_x_ratio ? _final_x_scale : _initial_x_scale;
+    float final_y_scale = _animate_y_ratio ? _final_y_scale : _initial_y_scale;
 
 
-    if (_animate_x_ratio) {
-      _base_x_scale = max(_initial_x_scale, _final_x_scale);
+    if (_animate_y_ratio) {
+      _base_y_scale = max(_initial_y_scale, _final_y_scale);
       array_format->add_column
       array_format->add_column
-        (InternalName::get_scale_x(), 1, qpGeomVertexColumn::NT_float32,
+        (InternalName::get_size(), 1, qpGeomVertexColumn::NT_float32,
          qpGeomVertexColumn::C_other);
          qpGeomVertexColumn::C_other);
     }
     }
 
 
-    if (_animate_y_ratio || _initial_y_scale != _base_y_scale) {
+    if (_aspect_ratio * _initial_x_scale != _initial_y_scale ||
+        _aspect_ratio * final_x_scale != final_y_scale) {
       array_format->add_column
       array_format->add_column
-        (InternalName::get_scale_y(), 1, qpGeomVertexColumn::NT_float32,
+        (InternalName::get_aspect_ratio(), 1, qpGeomVertexColumn::NT_float32,
          qpGeomVertexColumn::C_other);
          qpGeomVertexColumn::C_other);
     }
     }
 
 
@@ -284,27 +355,28 @@ init_geoms() {
     _vdata = new qpGeomVertexData
     _vdata = new qpGeomVertexData
       ("particles", format, qpGeomUsageHint::UH_dynamic);
       ("particles", format, qpGeomUsageHint::UH_dynamic);
     qpgeom->set_vertex_data(_vdata);
     qpgeom->set_vertex_data(_vdata);
-    _sprites = new qpGeomSprites(qpGeomUsageHint::UH_dynamic);
+    _sprites = new qpGeomPoints(qpGeomUsageHint::UH_dynamic);
     qpgeom->add_primitive(_sprites);
     qpgeom->add_primitive(_sprites);
 
 
-    state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _base_x_scale * texel_scale, true));
+    state = state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _base_y_scale * _height, true));
 
 
     if (_texture != (Texture *)NULL) {
     if (_texture != (Texture *)NULL) {
       state = state->add_attrib(TextureAttrib::make(_texture));
       state = state->add_attrib(TextureAttrib::make(_texture));
-    }
-
-    // Build a matrix to convert the texture coordinates to the ll, ur
-    // space.
-    LPoint2f ul(_ll_uv[0], _ur_uv[1]);
-    LPoint2f lr(_ur_uv[0], _ll_uv[1]);
-    LVector2f sc = lr - ul;
+      state = state->add_attrib(TexGenAttrib::make(TextureStage::get_default(), TexGenAttrib::M_point_sprite));
 
 
-    LMatrix4f mat
-      (sc[0], 0.0f, 0.0f, 0.0f,
-       0.0f, sc[1], 0.0f, 0.0f,
-       0.0f, 0.0f,  1.0f, 0.0f,
-       ul[0], ul[1], 0.0f, 1.0f);
-    state = state->add_attrib(TexMatrixAttrib::make(mat));
+      // Build a matrix to convert the texture coordinates to the ll, ur
+      // space.
+      LPoint2f ul(_ll_uv[0], _ur_uv[1]);
+      LPoint2f lr(_ur_uv[0], _ll_uv[1]);
+      LVector2f sc = lr - ul;
+      
+      LMatrix4f mat
+        (sc[0], 0.0f, 0.0f, 0.0f,
+         0.0f, sc[1], 0.0f, 0.0f,
+         0.0f, 0.0f,  1.0f, 0.0f,
+         ul[0], ul[1], 0.0f, 1.0f);
+      state = state->add_attrib(TexMatrixAttrib::make(mat));
+    }
 
 
   } else {
   } else {
     PT(GeomSprite) sprite = new GeomSprite(get_texture());
     PT(GeomSprite) sprite = new GeomSprite(get_texture());
@@ -366,8 +438,8 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {
   qpGeomVertexWriter vertex(_vdata, InternalName::get_vertex());
   qpGeomVertexWriter vertex(_vdata, InternalName::get_vertex());
   qpGeomVertexWriter color(_vdata, InternalName::get_color());
   qpGeomVertexWriter color(_vdata, InternalName::get_color());
   qpGeomVertexWriter rotate(_vdata, InternalName::get_rotate());
   qpGeomVertexWriter rotate(_vdata, InternalName::get_rotate());
-  qpGeomVertexWriter scale_x(_vdata, InternalName::get_scale_x());
-  qpGeomVertexWriter scale_y(_vdata, InternalName::get_scale_y());
+  qpGeomVertexWriter size(_vdata, InternalName::get_size());
+  qpGeomVertexWriter aspect_ratio(_vdata, InternalName::get_aspect_ratio());
 
 
   if (!use_qpgeom) {
   if (!use_qpgeom) {
     if (!_animate_x_ratio)
     if (!_animate_x_ratio)
@@ -435,32 +507,30 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {
       vertex.add_data3f(position);
       vertex.add_data3f(position);
       color.add_data4f(c);
       color.add_data4f(c);
 
 
-      if (_animate_x_ratio) {
-        float t = cur_particle->get_parameterized_age();
-        
-        if (_blend_method == PP_BLEND_CUBIC)
-          t = CUBIC_T(t);
-        
-        float scale = (_initial_x_scale +
-                       (t * (_final_x_scale - _initial_x_scale)));
-        scale_x.add_data1f(scale / _base_x_scale);
-
-      } else if (scale_x.has_column()) {
-        scale_x.add_data1f(_initial_x_scale / _base_x_scale);
-      }
+      float current_x_scale = _initial_x_scale;
+      float current_y_scale = _initial_y_scale;
 
 
-      if (_animate_y_ratio) {
+      if (_animate_x_ratio || _animate_y_ratio) {
         float t = cur_particle->get_parameterized_age();
         float t = cur_particle->get_parameterized_age();
-        
-        if (_blend_method == PP_BLEND_CUBIC)
+        if (_blend_method == PP_BLEND_CUBIC) {
           t = CUBIC_T(t);
           t = CUBIC_T(t);
-        
-        float scale = (_initial_y_scale +
-                       (t * (_final_y_scale - _initial_y_scale)));
-        scale_y.add_data1f(scale / _base_y_scale);
-
-      } else if (scale_y.has_column()) {
-        scale_y.add_data1f(_initial_y_scale / _base_y_scale);
+        }
+
+        if (_animate_x_ratio) {
+          current_x_scale = (_initial_x_scale +
+                             (t * (_final_x_scale - _initial_x_scale)));
+        }
+        if (_animate_y_ratio) {
+          current_y_scale = (_initial_y_scale +
+                             (t * (_final_y_scale - _initial_y_scale)));
+        }
+      }
+       
+      if (size.has_column()) {
+        size.add_data1f(current_y_scale * _height);
+      }
+      if (aspect_ratio.has_column()) {
+        aspect_ratio.add_data1f(_aspect_ratio * current_x_scale / current_y_scale);
       }
       }
 
 
       if (_animate_theta) {
       if (_animate_theta) {

+ 9 - 5
panda/src/particlesystem/spriteParticleRenderer.h

@@ -29,7 +29,7 @@
 #include "geom.h"
 #include "geom.h"
 #include "geomSprite.h"
 #include "geomSprite.h"
 #include "qpgeomVertexData.h"
 #include "qpgeomVertexData.h"
-#include "qpgeomSprites.h"
+#include "qpgeomPoints.h"
 
 
 class NodePath;
 class NodePath;
 
 
@@ -56,11 +56,12 @@ PUBLISHED:
 
 
   INLINE SourceType get_source_type() const;
   INLINE SourceType get_source_type() const;
 
 
-  void set_from_node(const NodePath &node_path);
+  void set_from_node(const NodePath &node_path, bool size_from_texels = true);
 
 
-  INLINE void set_texture(Texture *tex);
+  INLINE void set_texture(Texture *tex, float texels_per_unit = 1.0);
   INLINE void set_ll_uv(const TexCoordf &ll_uv);
   INLINE void set_ll_uv(const TexCoordf &ll_uv);
   INLINE void set_ur_uv(const TexCoordf &ur_uv);
   INLINE void set_ur_uv(const TexCoordf &ur_uv);
+  INLINE void set_size(float width, float height);
   INLINE void set_color(const Colorf &color);
   INLINE void set_color(const Colorf &color);
   INLINE void set_x_scale_flag(bool animate_x_ratio);
   INLINE void set_x_scale_flag(bool animate_x_ratio);
   INLINE void set_y_scale_flag(bool animate_y_ratio);
   INLINE void set_y_scale_flag(bool animate_y_ratio);
@@ -76,6 +77,8 @@ PUBLISHED:
   INLINE Texture *get_texture() const;
   INLINE Texture *get_texture() const;
   INLINE const TexCoordf &get_ll_uv() const;
   INLINE const TexCoordf &get_ll_uv() const;
   INLINE const TexCoordf &get_ur_uv() const;
   INLINE const TexCoordf &get_ur_uv() const;
+  INLINE float get_width() const;
+  INLINE float get_height() const;
   INLINE Colorf get_color() const;
   INLINE Colorf get_color() const;
   INLINE bool get_x_scale_flag() const;
   INLINE bool get_x_scale_flag() const;
   INLINE bool get_y_scale_flag() const;
   INLINE bool get_y_scale_flag() const;
@@ -93,7 +96,7 @@ PUBLISHED:
 
 
 private:
 private:
   PT(Geom) _sprite_primitive;
   PT(Geom) _sprite_primitive;
-  PT(qpGeomSprites) _sprites;
+  PT(qpGeomPoints) _sprites;
   PT(Texture) _texture;
   PT(Texture) _texture;
 
 
   PTA_Vertexf _vertex_array;
   PTA_Vertexf _vertex_array;
@@ -107,12 +110,13 @@ private:
   Colorf _color;
   Colorf _color;
 
 
   TexCoordf _ll_uv, _ur_uv;
   TexCoordf _ll_uv, _ur_uv;
+  float _height;
+  float _width;
   float _initial_x_scale;
   float _initial_x_scale;
   float _final_x_scale;
   float _final_x_scale;
   float _initial_y_scale;
   float _initial_y_scale;
   float _final_y_scale;
   float _final_y_scale;
   float _theta;
   float _theta;
-  float _base_x_scale;
   float _base_y_scale;
   float _base_y_scale;
   float _aspect_ratio;
   float _aspect_ratio;
 
 

+ 2 - 2
panda/src/pgraph/binCullHandler.cxx

@@ -26,6 +26,6 @@
 //               discovered by the CullTraverser.
 //               discovered by the CullTraverser.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void BinCullHandler::
 void BinCullHandler::
-record_object(CullableObject *object) {
-  _cull_result->add_object(object);
+record_object(CullableObject *object, const CullTraverser *traverser) {
+  _cull_result->add_object(object, traverser);
 }
 }

+ 2 - 1
panda/src/pgraph/binCullHandler.h

@@ -35,7 +35,8 @@ class EXPCL_PANDA BinCullHandler : public CullHandler {
 public:
 public:
   INLINE BinCullHandler(CullResult *cull_result);
   INLINE BinCullHandler(CullResult *cull_result);
 
 
-  virtual void record_object(CullableObject *object);
+  virtual void record_object(CullableObject *object, 
+                             const CullTraverser *traverser);
 
 
 private:
 private:
   PT(CullResult) _cull_result;
   PT(CullResult) _cull_result;

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

@@ -44,7 +44,7 @@ CullHandler::
 //               later.
 //               later.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullHandler::
 void CullHandler::
-record_object(CullableObject *object) {
+record_object(CullableObject *object, const CullTraverser *traverser) {
   nout << *object->_geom << " " << *object->_transform << " " 
   nout << *object->_geom << " " << *object->_transform << " " 
        << *object->_state << "\n";
        << *object->_state << "\n";
   delete object;
   delete object;

+ 4 - 1
panda/src/pgraph/cullHandler.h

@@ -23,6 +23,8 @@
 #include "cullableObject.h"
 #include "cullableObject.h"
 #include "graphicsStateGuardianBase.h"
 #include "graphicsStateGuardianBase.h"
 
 
+class CullTraverser;
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : CullHandler
 //       Class : CullHandler
 // Description : This defines the abstract interface for an object
 // Description : This defines the abstract interface for an object
@@ -34,7 +36,8 @@ class EXPCL_PANDA CullHandler {
 public:
 public:
   virtual ~CullHandler();
   virtual ~CullHandler();
 
 
-  virtual void record_object(CullableObject *object);
+  virtual void record_object(CullableObject *object, 
+                             const CullTraverser *traverser);
 
 
   INLINE static void draw(CullableObject *object,
   INLINE static void draw(CullableObject *object,
                           GraphicsStateGuardianBase *gsg);
                           GraphicsStateGuardianBase *gsg);

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

@@ -84,7 +84,7 @@ make_next() const {
 //               pointer, and will eventually delete it.
 //               pointer, and will eventually delete it.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullResult::
 void CullResult::
-add_object(CullableObject *object) {
+add_object(CullableObject *object, const CullTraverser *traverser) {
   // Check to see if there's a special transparency setting.
   // Check to see if there's a special transparency setting.
   const RenderState *state = object->_state;
   const RenderState *state = object->_state;
   nassertv(state != (const RenderState *)NULL);
   nassertv(state != (const RenderState *)NULL);
@@ -128,7 +128,8 @@ add_object(CullableObject *object) {
               get_dual_transparent_state_decals() : 
               get_dual_transparent_state_decals() : 
               get_dual_transparent_state();
               get_dual_transparent_state();
             transparent_part->_state = state->compose(transparent_state);
             transparent_part->_state = state->compose(transparent_state);
-            transparent_part->munge_geom(get_geom_munger(transparent_part->_state));
+            transparent_part->munge_geom(get_geom_munger(transparent_part->_state),
+                                         traverser);
             CullBin *bin = get_bin(transparent_part->_state->get_bin_index());
             CullBin *bin = get_bin(transparent_part->_state->get_bin_index());
             nassertv(bin != (CullBin *)NULL);
             nassertv(bin != (CullBin *)NULL);
             bin->add_object(transparent_part);
             bin->add_object(transparent_part);
@@ -155,7 +156,7 @@ add_object(CullableObject *object) {
 
 
   // Munge vertices as needed for the GSG's requirements, and the
   // Munge vertices as needed for the GSG's requirements, and the
   // object's current state.
   // object's current state.
-  object->munge_geom(get_geom_munger(object->_state));
+  object->munge_geom(get_geom_munger(object->_state), traverser);
   
   
   CullBin *bin = get_bin(object->_state->get_bin_index());
   CullBin *bin = get_bin(object->_state->get_bin_index());
   nassertv(bin != (CullBin *)NULL);
   nassertv(bin != (CullBin *)NULL);

+ 2 - 1
panda/src/pgraph/cullResult.h

@@ -33,6 +33,7 @@
 
 
 
 
 class GraphicsStateGuardianBase;
 class GraphicsStateGuardianBase;
+class CullTraverser;
 class TransformState;
 class TransformState;
 class RenderState;
 class RenderState;
 
 
@@ -56,7 +57,7 @@ public:
 
 
   INLINE CullBin *get_bin(int bin_index);
   INLINE CullBin *get_bin(int bin_index);
 
 
-  void add_object(CullableObject *object);
+  void add_object(CullableObject *object, const CullTraverser *traverser);
   void finish_cull();
   void finish_cull();
   void draw();
   void draw();
 
 

+ 10 - 0
panda/src/pgraph/cullTraverser.I

@@ -17,6 +17,16 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::get_gsg
+//       Access: Public
+//  Description: Returns the GraphicsStateGuardian in effect.
+////////////////////////////////////////////////////////////////////
+INLINE GraphicsStateGuardianBase *CullTraverser::
+get_gsg() const {
+  return _gsg;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::set_scene
 //     Function: CullTraverser::set_scene
 //       Access: Public
 //       Access: Public

+ 8 - 5
panda/src/pgraph/cullTraverser.cxx

@@ -49,7 +49,9 @@ TypeHandle CullTraverser::_type_handle;
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CullTraverser::
 CullTraverser::
-CullTraverser() {
+CullTraverser(GraphicsStateGuardianBase *gsg) :
+  _gsg(gsg)
+{
   _camera_mask = DrawMask::all_on();
   _camera_mask = DrawMask::all_on();
   _has_tag_state_key = false;
   _has_tag_state_key = false;
   _initial_state = RenderState::make_empty();
   _initial_state = RenderState::make_empty();
@@ -65,6 +67,7 @@ CullTraverser() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CullTraverser::
 CullTraverser::
 CullTraverser(const CullTraverser &copy) :
 CullTraverser(const CullTraverser &copy) :
+  _gsg(copy._gsg),
   _scene_setup(copy._scene_setup),
   _scene_setup(copy._scene_setup),
   _camera_mask(copy._camera_mask),
   _camera_mask(copy._camera_mask),
   _has_tag_state_key(copy._has_tag_state_key),
   _has_tag_state_key(copy._has_tag_state_key),
@@ -217,7 +220,7 @@ traverse_below(CullTraverserData &data) {
       _geoms_pcollector.add_level(num_geoms);
       _geoms_pcollector.add_level(num_geoms);
       for (int i = 0; i < num_geoms; i++) {
       for (int i = 0; i < num_geoms; i++) {
         CullableObject *object = new CullableObject(data, geom_node, i);
         CullableObject *object = new CullableObject(data, geom_node, i);
-        _cull_handler->record_object(object);
+        _cull_handler->record_object(object, this);
       }
       }
     }
     }
 
 
@@ -273,12 +276,12 @@ show_bounds(CullTraverserData &data) {
     CullableObject *outer_viz = 
     CullableObject *outer_viz = 
       new CullableObject(bounds_viz, get_bounds_outer_viz_state(), 
       new CullableObject(bounds_viz, get_bounds_outer_viz_state(), 
                          data._render_transform);
                          data._render_transform);
-    _cull_handler->record_object(outer_viz);
+    _cull_handler->record_object(outer_viz, this);
 
 
     CullableObject *inner_viz = 
     CullableObject *inner_viz = 
       new CullableObject(bounds_viz, get_bounds_inner_viz_state(), 
       new CullableObject(bounds_viz, get_bounds_inner_viz_state(), 
                          data._render_transform);
                          data._render_transform);
-    _cull_handler->record_object(inner_viz);
+    _cull_handler->record_object(inner_viz, this);
   }
   }
 }
 }
 
 
@@ -483,7 +486,7 @@ start_decal(const CullTraverserData &data) {
     // Finally, send the whole list down to the CullHandler for
     // Finally, send the whole list down to the CullHandler for
     // processing.  The first Geom in the node now represents the
     // processing.  The first Geom in the node now represents the
     // overall state.
     // overall state.
-    _cull_handler->record_object(object);
+    _cull_handler->record_object(object, this);
   }
   }
 }
 }
 
 

+ 5 - 1
panda/src/pgraph/cullTraverser.h

@@ -32,6 +32,7 @@
 #include "typedObject.h"
 #include "typedObject.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
 
 
+class GraphicsStateGuardian;
 class PandaNode;
 class PandaNode;
 class CullHandler;
 class CullHandler;
 class CullableObject;
 class CullableObject;
@@ -49,9 +50,11 @@ class NodePath;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA CullTraverser : public TypedObject {
 class EXPCL_PANDA CullTraverser : public TypedObject {
 public:
 public:
-  CullTraverser();
+  CullTraverser(GraphicsStateGuardianBase *gsg);
   CullTraverser(const CullTraverser &copy);
   CullTraverser(const CullTraverser &copy);
 
 
+  INLINE GraphicsStateGuardianBase *get_gsg() const;
+
   INLINE void set_scene(SceneSetup *scene_setup);
   INLINE void set_scene(SceneSetup *scene_setup);
   INLINE SceneSetup *get_scene() const;
   INLINE SceneSetup *get_scene() const;
   INLINE bool has_tag_state_key() const;
   INLINE bool has_tag_state_key() const;
@@ -103,6 +106,7 @@ private:
   CullableObject *r_get_decals(CullTraverserData &data,
   CullableObject *r_get_decals(CullTraverserData &data,
                                CullableObject *decals);
                                CullableObject *decals);
 
 
+  GraphicsStateGuardianBase *_gsg;
   PT(SceneSetup) _scene_setup;
   PT(SceneSetup) _scene_setup;
   DrawMask _camera_mask;
   DrawMask _camera_mask;
   bool _has_tag_state_key;
   bool _has_tag_state_key;

+ 22 - 0
panda/src/pgraph/cullableObject.I

@@ -163,3 +163,25 @@ INLINE int CullableObject::
 get_num_ever_allocated() {
 get_num_ever_allocated() {
   return _num_ever_allocated;
   return _num_ever_allocated;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::SortPoints::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullableObject::SortPoints::
+SortPoints(const CullableObject::PointData *array) :
+  _array(array)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::SortPoints::operator ()
+//       Access: Public
+//  Description: Orders the points from back-to-front for correct
+//               transparency sorting in munge_points_to_quads
+////////////////////////////////////////////////////////////////////
+INLINE bool CullableObject::SortPoints::
+operator () (unsigned short a, unsigned short b) const {
+  return _array[a]._dist > _array[b]._dist;
+}

+ 312 - 7
panda/src/pgraph/cullableObject.cxx

@@ -20,7 +20,15 @@
 #include "textureAttrib.h"
 #include "textureAttrib.h"
 #include "renderState.h"
 #include "renderState.h"
 #include "clockObject.h"
 #include "clockObject.h"
+#include "cullTraverser.h"
+#include "sceneSetup.h"
+#include "lens.h"
+#include "pStatTimer.h"
+#include "qpgeomVertexWriter.h"
+#include "qpgeomVertexReader.h"
+#include "qpgeomTriangles.h"
 
 
+PStatCollector CullableObject::_munge_points_pcollector("Cull:Munge:Points");
 
 
 CullableObject *CullableObject::_deleted_chain = (CullableObject *)NULL;
 CullableObject *CullableObject::_deleted_chain = (CullableObject *)NULL;
 int CullableObject::_num_ever_allocated = 0;
 int CullableObject::_num_ever_allocated = 0;
@@ -33,13 +41,29 @@ TypeHandle CullableObject::_type_handle;
 //               and/or its vertices.
 //               and/or its vertices.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullableObject::
 void CullableObject::
-munge_geom(const qpGeomMunger *munger) {
+munge_geom(const qpGeomMunger *munger, const CullTraverser *traverser) {
   if (_geom != (Geom *)NULL) {
   if (_geom != (Geom *)NULL) {
     // Temporary test and dcast until the experimental Geom rewrite
     // Temporary test and dcast until the experimental Geom rewrite
     // becomes the actual Geom rewrite.
     // becomes the actual Geom rewrite.
     if (_geom->is_of_type(qpGeom::get_class_type())) {
     if (_geom->is_of_type(qpGeom::get_class_type())) {
       _munger = munger;
       _munger = munger;
       CPT(qpGeom) qpgeom = DCAST(qpGeom, _geom);
       CPT(qpGeom) qpgeom = DCAST(qpGeom, _geom);
+      _munged_data = qpgeom->get_vertex_data();
+
+      int point_rendering = _state->get_point_rendering(qpgeom->get_point_rendering());
+
+      GraphicsStateGuardianBase *gsg = traverser->get_gsg();
+      if ((point_rendering & ~gsg->get_supported_point_rendering()) != 0) {
+        // The GSG doesn't support rendering these fancy points
+        // directly; we have to render them in software instead.
+        // Munge them into quads.  This will replace the _geom and
+        // _munged_data, and might also replace _state.
+        munge_points_to_quads(traverser);
+        qpgeom = DCAST(qpGeom, _geom);
+      }
+
+      // Now invoke the munger to ensure the resulting geometry is in
+      // a GSG-friendly form.
       qpgeom->munge_geom(munger, qpgeom, _munged_data);
       qpgeom->munge_geom(munger, qpgeom, _munged_data);
       CPT(qpGeomVertexData) animated_vertices = 
       CPT(qpGeomVertexData) animated_vertices = 
         _munged_data->animate_vertices_cull();
         _munged_data->animate_vertices_cull();
@@ -48,11 +72,18 @@ munge_geom(const qpGeomMunger *munger) {
         // These vertices were CPU-animated, so flash them.
         // These vertices were CPU-animated, so flash them.
         static const double flash_rate = 1.0;  // 1 state change per second
         static const double flash_rate = 1.0;  // 1 state change per second
         int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * flash_rate);
         int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * flash_rate);
-        if ((cycle & 3) == 0) {
-          animated_vertices = animated_vertices->set_color(Colorf(0.8f, 0.2f, 0.2f, 1.0f));
-          _state = _state->remove_attrib(TextureAttrib::get_class_type());
-        } else if ((cycle & 3) == 2) {
-          animated_vertices = animated_vertices->set_color(Colorf(0.1f, 0.2f, 0.8f, 1.0f));
+        if ((cycle & 2) == 0) {
+          static Colorf flash_color(0.8f, 0.2f, 0.2f, 1.0f);
+          if (animated_vertices->has_color()) {
+            animated_vertices = animated_vertices->set_color(flash_color);
+          } else {
+            // We have to add a color column, which means we have to
+            // re-munge.
+            animated_vertices = animated_vertices->set_color
+              (flash_color, 1, qpGeomVertexColumn::NT_packed_dabc,
+               qpGeomVertexColumn::C_color);
+            animated_vertices = munger->munge_data(animated_vertices);
+          }
           _state = _state->remove_attrib(TextureAttrib::get_class_type());
           _state = _state->remove_attrib(TextureAttrib::get_class_type());
         }
         }
       }
       }
@@ -62,7 +93,7 @@ munge_geom(const qpGeomMunger *munger) {
     }
     }
   }
   }
   if (_next != (CullableObject *)NULL) {
   if (_next != (CullableObject *)NULL) {
-    _next->munge_geom(munger);
+    _next->munge_geom(munger, traverser);
   }
   }
 }
 }
 
 
@@ -91,3 +122,277 @@ output(ostream &out) const {
     out << "(null)";
     out << "(null)";
   }
   }
 }
 }
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::munge_points_to_quads
+//       Access: Private
+//  Description: Converts a table of points to quads for rendering on
+//               systems that don't support fancy points.
+//
+//               This may replace _geom, _munged_data, and _state.
+////////////////////////////////////////////////////////////////////
+void CullableObject::
+munge_points_to_quads(const CullTraverser *traverser) {
+  PStatTimer timer(_munge_points_pcollector);
+
+  GraphicsStateGuardianBase *gsg = traverser->get_gsg();
+  CPT(qpGeom) qpgeom = DCAST(qpGeom, _geom);
+
+  qpGeomVertexReader vertex(_munged_data, InternalName::get_vertex());
+  qpGeomVertexReader normal(_munged_data, InternalName::get_normal());
+  qpGeomVertexReader color(_munged_data, InternalName::get_color());
+  qpGeomVertexReader texcoord(_munged_data, InternalName::get_texcoord());
+  qpGeomVertexReader rotate(_munged_data, InternalName::get_rotate());
+  qpGeomVertexReader size(_munged_data, InternalName::get_size());
+  qpGeomVertexReader aspect_ratio(_munged_data, InternalName::get_aspect_ratio());
+
+  bool has_normal = (normal.has_column());
+  bool has_color = (color.has_column());
+  bool has_texcoord = (texcoord.has_column());
+  bool has_rotate = (rotate.has_column());
+  bool has_size = (size.has_column());
+  bool has_aspect_ratio = (aspect_ratio.has_column());
+
+  bool sprite_texcoord = false;
+  const TexGenAttrib *tex_gen = _state->get_tex_gen();
+  if (tex_gen != (TexGenAttrib *)NULL) {
+    if (tex_gen->get_mode(TextureStage::get_default()) == TexGenAttrib::M_point_sprite) {
+      sprite_texcoord = true;
+
+      // Turn off the TexGenAttrib, since we don't want it now.
+      _state = _state->add_attrib(tex_gen->remove_stage(TextureStage::get_default()));
+    }
+  }
+
+  PT(qpGeomVertexArrayFormat) new_array_format =
+    new qpGeomVertexArrayFormat(InternalName::get_vertex(), 4, 
+                                qpGeomVertexColumn::NT_float32,
+                                qpGeomVertexColumn::C_clip_point);
+  if (has_normal) {
+    const qpGeomVertexColumn *c = normal.get_column();
+    new_array_format->add_column
+      (InternalName::get_normal(), c->get_num_components(),
+       c->get_numeric_type(), c->get_contents());
+  }
+  if (has_color) {
+    const qpGeomVertexColumn *c = color.get_column();
+    new_array_format->add_column
+      (InternalName::get_color(), c->get_num_components(),
+       c->get_numeric_type(), c->get_contents());
+  }
+  if (sprite_texcoord) {
+    new_array_format->add_column
+      (InternalName::get_texcoord(), 2,
+       qpGeomVertexColumn::NT_float32,
+       qpGeomVertexColumn::C_texcoord);
+
+  } else if (has_texcoord) {
+    const qpGeomVertexColumn *c = texcoord.get_column();
+    new_array_format->add_column
+      (InternalName::get_texcoord(), c->get_num_components(),
+       c->get_numeric_type(), c->get_contents());
+  }
+
+  CPT(qpGeomVertexFormat) new_format = 
+    qpGeomVertexFormat::register_format(new_array_format);
+
+  PT(qpGeomVertexData) new_data = new qpGeomVertexData
+    (_munged_data->get_name(), new_format, qpGeomUsageHint::UH_client);
+
+  qpGeomVertexWriter new_vertex(new_data, InternalName::get_vertex());
+  qpGeomVertexWriter new_normal(new_data, InternalName::get_normal());
+  qpGeomVertexWriter new_color(new_data, InternalName::get_color());
+  qpGeomVertexWriter new_texcoord(new_data, InternalName::get_texcoord());
+  int new_vi = 0;
+
+  PT(qpGeom) new_geom = new qpGeom();
+  new_geom->set_vertex_data(new_data);
+
+  const LMatrix4f &modelview = _transform->get_mat();
+
+  SceneSetup *scene = traverser->get_scene();
+  const Lens *lens = scene->get_lens();
+  const LMatrix4f &lens_mat = lens->get_projection_mat();
+  LMatrix4f projection =
+    LMatrix4f::convert_mat(CS_yup_right, lens->get_coordinate_system()) *
+    lens_mat;
+
+  LMatrix4f render_transform;
+  if (has_normal) {
+    render_transform = modelview * projection;
+  }
+
+  int viewport_width = scene->get_viewport_width();
+  int viewport_height = scene->get_viewport_height();
+
+  float point_size = 1.0f;
+  bool perspective = false;
+  const RenderModeAttrib *render_mode = _state->get_render_mode();
+  if (render_mode != (RenderModeAttrib *)NULL) {
+    point_size = render_mode->get_thickness();
+    perspective = render_mode->get_perspective();
+
+    if (render_mode->get_mode() != RenderModeAttrib::M_filled) {
+      // Be sure to turn on polygon render mode, since we're actually
+      // rendering polygons, not points any more.
+      _state = _state->add_attrib(RenderModeAttrib::make(RenderModeAttrib::M_filled));
+    }
+  }
+
+  // Replace each primitive in the Geom (it's presumably a GeomPoints
+  // primitive, although it might be some other kind of primitive if
+  // we got here because RenderModeAttrib::M_point is enabled) with a
+  // new primitive that replaces each vertex with a quad of the
+  // appropriate scale and orientation.
+
+  // BUG: if we're rendering polygons in M_point mode with a
+  // CullFaceAttrib in effect, we won't actually apply the
+  // CullFaceAttrib but will always render all of the vertices of the
+  // polygons.  This is certainly a bug, but in order to fix it we'd
+  // have to do the face culling ourselves--not sure if it's worth it.
+  int num_primitives = qpgeom->get_num_primitives();
+  for (int pi = 0; pi < num_primitives; ++pi) {
+    const qpGeomPrimitive *primitive = qpgeom->get_primitive(pi);
+
+    // We must first convert all of the points to eye space.
+    int num_points = primitive->get_max_vertex() + 1;
+    int num_vertices = primitive->get_num_vertices();
+    PointData *points = (PointData *)alloca(num_points * sizeof(PointData));
+    unsigned short *vertices = (unsigned short *)alloca(num_vertices * sizeof(unsigned short));
+    unsigned short *vertices_end = vertices + num_vertices;
+
+    memcpy(vertices, primitive->get_vertices(),
+           num_vertices * sizeof(unsigned short));
+    
+    unsigned short *vi;
+    for (vi = vertices; vi != vertices_end; ++vi) {
+      // Get the point in eye-space coordinates.
+      vertex.set_vertex(*vi);
+      points[*vi]._eye = modelview.xform_point(vertex.get_data3f());
+      points[*vi]._dist = gsg->compute_distance_to(points[*vi]._eye);
+    }
+  
+    // Now sort the points in order from back-to-front so they will
+    // render properly with transparency, at least with each other.
+    sort(vertices, vertices_end, SortPoints(points));
+  
+    // Go through the points, now in sorted order, and generate a pair
+    // of triangles for each one.  We generate indexed triangles
+    // instead of two-triangle strips, since this seems to be
+    // generally faster on PC hardware (otherwise, we'd have to nearly
+    // double the vertices to stitch all the little triangle strips
+    // together).
+    PT(qpGeomPrimitive) new_primitive = new qpGeomTriangles(qpGeomUsageHint::UH_client);
+
+    for (vi = vertices; vi != vertices_end; ++vi) {
+      // The point in eye coordinates.
+      const LPoint3f &eye = points[*vi]._eye;
+    
+      // The point in clip coordinates.
+      LPoint4f p4 = LPoint4f(eye[0], eye[1], eye[2], 1.0f) * projection;
+
+      if (has_size) {
+        size.set_vertex(*vi);
+        point_size = size.get_data1f();
+      }
+
+      float scale_y = point_size;
+      if (perspective) {
+        // Perspective-sized points.  Here point_size is a width in 3-d
+        // units.  To arrange that, we need to figure out the appropriate
+        // scaling factor based on the current viewport and projection
+        // matrix.
+        LVector3f height(0.0f, point_size, 1.0f);
+        height = height * projection;
+        scale_y = height[1] * viewport_height;
+
+        // We should then divide the radius by the distance from the
+        // camera plane, to emulate the glPointParameters() behavior.
+        scale_y /= gsg->compute_distance_to(eye);
+      }
+      
+      // Also factor in the homogeneous scale for being in clip
+      // coordinates still.
+      scale_y *= p4[3];
+
+      float scale_x = scale_y;
+      if (has_aspect_ratio) {
+        aspect_ratio.set_vertex(*vi);
+        scale_x *= aspect_ratio.get_data1f();
+      }
+
+      // Define the first two corners based on the scales in X and Y.
+      LPoint2f c0(scale_x, scale_y);
+      LPoint2f c1(-scale_x, scale_y);
+
+      if (has_rotate) {
+        // If we have a rotate factor, apply it to those two corners.
+        rotate.set_vertex(*vi);
+        float r = rotate.get_data1f();
+        LMatrix3f mat = LMatrix3f::rotate_mat(r);
+        c0 = c0 * mat;
+        c1 = c1 * mat;
+      }
+
+      // Finally, scale the corners in their newly-rotated position,
+      // to compensate for the aspect ratio of the viewport.
+      float rx = 1.0f / viewport_width;
+      float ry = 1.0f / viewport_height;
+      c0.set(c0[0] * rx, c0[1] * ry);
+      c1.set(c1[0] * rx, c1[1] * ry);
+
+      new_vertex.add_data4f(p4[0] + c0[0], p4[1] + c0[1], p4[2], p4[3]);
+      new_vertex.add_data4f(p4[0] + c1[0], p4[1] + c1[1], p4[2], p4[3]);
+      new_vertex.add_data4f(p4[0] - c1[0], p4[1] - c1[1], p4[2], p4[3]);
+      new_vertex.add_data4f(p4[0] - c0[0], p4[1] - c0[1], p4[2], p4[3]);
+
+      if (has_normal) {
+        normal.set_vertex(*vi);
+        Normalf c = render_transform.xform_vec(normal.get_data3f());
+        new_normal.add_data3f(c);
+        new_normal.add_data3f(c);
+        new_normal.add_data3f(c);
+        new_normal.add_data3f(c);
+      }
+      if (has_color) {
+        color.set_vertex(*vi);
+        const Colorf &c = color.get_data4f();
+        new_color.add_data4f(c);
+        new_color.add_data4f(c);
+        new_color.add_data4f(c);
+        new_color.add_data4f(c);
+      }
+      if (sprite_texcoord) {
+        new_texcoord.add_data2f(1.0f, 0.0f);
+        new_texcoord.add_data2f(0.0f, 0.0f);
+        new_texcoord.add_data2f(1.0f, 1.0f);
+        new_texcoord.add_data2f(0.0f, 1.0f);
+      } else if (has_texcoord) {
+        texcoord.set_vertex(*vi);
+        const LVecBase4f &c = texcoord.get_data4f();
+        new_texcoord.add_data4f(c);
+        new_texcoord.add_data4f(c);
+        new_texcoord.add_data4f(c);
+        new_texcoord.add_data4f(c);
+      }
+
+      new_primitive->add_vertex(new_vi);
+      new_primitive->add_vertex(new_vi + 1);
+      new_primitive->add_vertex(new_vi + 2);
+      new_primitive->close_primitive();
+
+      new_primitive->add_vertex(new_vi + 2);
+      new_primitive->add_vertex(new_vi + 1);
+      new_primitive->add_vertex(new_vi + 3);
+      new_primitive->close_primitive();
+
+      new_vi += 4;
+    }
+
+    new_geom->add_primitive(new_primitive);
+  }
+
+  _geom = new_geom.p();
+  _munged_data = new_data;
+}

+ 23 - 1
panda/src/pgraph/cullableObject.h

@@ -31,6 +31,9 @@
 #include "referenceCount.h"
 #include "referenceCount.h"
 #include "geomNode.h"
 #include "geomNode.h"
 #include "cullTraverserData.h"
 #include "cullTraverserData.h"
+#include "pStatCollector.h"
+
+class CullTraverser;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : CullableObject
 //       Class : CullableObject
@@ -54,7 +57,7 @@ public:
 
 
   INLINE bool has_decals() const;
   INLINE bool has_decals() const;
 
 
-  void munge_geom(const qpGeomMunger *munger);
+  void munge_geom(const qpGeomMunger *munger, const CullTraverser *traverser);
   INLINE void draw(GraphicsStateGuardianBase *gsg);
   INLINE void draw(GraphicsStateGuardianBase *gsg);
 
 
 public:
 public:
@@ -80,9 +83,28 @@ public:
   CullableObject *_next;
   CullableObject *_next;
 
 
 private:
 private:
+  void munge_points_to_quads(const CullTraverser *traverser);
+
+private:
+  // This class is used internally by munge_points_to_quads().
+  class PointData {
+  public:
+    LPoint3f _eye;
+    float _dist;
+  };
+  class SortPoints {
+  public:
+    INLINE SortPoints(const PointData *array);
+    INLINE bool operator ()(unsigned short a, unsigned short b) const;
+
+    const PointData *_array;
+  };
+
   static CullableObject *_deleted_chain;
   static CullableObject *_deleted_chain;
   static int _num_ever_allocated;
   static int _num_ever_allocated;
 
 
+  static PStatCollector _munge_points_pcollector;
+
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;

+ 2 - 2
panda/src/pgraph/drawCullHandler.cxx

@@ -32,10 +32,10 @@
 //               discovered by the CullTraverser.
 //               discovered by the CullTraverser.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DrawCullHandler::
 void DrawCullHandler::
-record_object(CullableObject *object) {
+record_object(CullableObject *object, const CullTraverser *traverser) {
   // Munge vertices as needed for the GSG's requirements, and the
   // Munge vertices as needed for the GSG's requirements, and the
   // object's current state.
   // object's current state.
-  object->munge_geom(_gsg->get_geom_munger(object->_state));
+  object->munge_geom(_gsg->get_geom_munger(object->_state), traverser);
 
 
   // And draw the object, then dispense with it.
   // And draw the object, then dispense with it.
   draw(object, _gsg);
   draw(object, _gsg);

+ 2 - 1
panda/src/pgraph/drawCullHandler.h

@@ -39,7 +39,8 @@ class EXPCL_PANDA DrawCullHandler : public CullHandler {
 public:
 public:
   INLINE DrawCullHandler(GraphicsStateGuardianBase *gsg);
   INLINE DrawCullHandler(GraphicsStateGuardianBase *gsg);
 
 
-  virtual void record_object(CullableObject *object);
+  virtual void record_object(CullableObject *object, 
+                             const CullTraverser *traverser);
 
 
 private:
 private:
   GraphicsStateGuardianBase *_gsg;
   GraphicsStateGuardianBase *_gsg;

+ 24 - 0
panda/src/pgraph/renderModeAttrib.I

@@ -71,3 +71,27 @@ INLINE bool RenderModeAttrib::
 get_perspective() const {
 get_perspective() const {
   return _perspective;
   return _perspective;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderModeAttrib::get_point_rendering
+//       Access: Published
+//  Description: Returns the union of the Geom::PointRendering bits
+//               that will be required once this RenderModeAttrib is
+//               applied to a geom which includes the indicated
+//               geom_point_rendering bits.
+////////////////////////////////////////////////////////////////////
+INLINE int RenderModeAttrib::
+get_point_rendering(int geom_point_rendering) const {
+  if (_mode == M_point) {
+    geom_point_rendering |= qpGeom::PR_point;
+  }
+  if ((geom_point_rendering & qpGeom::PR_point) != 0) {
+    if (_perspective) {
+      geom_point_rendering |= (qpGeom::PR_perspective | qpGeom::PR_uniform_size);
+    } else if (_thickness != 1.0f) {
+      geom_point_rendering |= qpGeom::PR_uniform_size;
+    }
+  }
+
+  return geom_point_rendering;
+}

+ 3 - 0
panda/src/pgraph/renderModeAttrib.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 #include "pandabase.h"
 
 
 #include "renderAttrib.h"
 #include "renderAttrib.h"
+#include "qpgeom.h"
 
 
 class FactoryParams;
 class FactoryParams;
 
 
@@ -49,6 +50,8 @@ PUBLISHED:
   INLINE float get_thickness() const;
   INLINE float get_thickness() const;
   INLINE bool get_perspective() const;
   INLINE bool get_perspective() const;
 
 
+  INLINE int get_point_rendering(int geom_point_rendering) const;
+
 public:
 public:
   virtual void issue(GraphicsStateGuardianBase *gsg) const;
   virtual void issue(GraphicsStateGuardianBase *gsg) const;
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;

+ 39 - 0
panda/src/pgraph/renderState.I

@@ -230,6 +230,45 @@ get_tex_gen() const {
   return _tex_gen;
   return _tex_gen;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::get_render_mode
+//       Access: Published
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a RenderModeAttrib on this state.  It returns a
+//               pointer to the RenderModeAttrib, if there is one, or
+//               NULL if there is not.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderModeAttrib *RenderState::
+get_render_mode() const {
+  if ((_flags & F_checked_render_mode) == 0) {
+    // We pretend this function is const, even though it transparently
+    // modifies the internal render_mode cache.
+    ((RenderState *)this)->determine_render_mode();
+  }
+  return _render_mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::get_point_rendering
+//       Access: Published
+//  Description: Returns the union of the Geom::PointRendering bits
+//               that will be required once this RenderState is
+//               applied to a geom which includes the indicated
+//               geom_point_rendering bits.
+////////////////////////////////////////////////////////////////////
+INLINE int RenderState::
+get_point_rendering(int geom_point_rendering) const {
+  if (get_render_mode() != (const RenderModeAttrib *)NULL) {
+    geom_point_rendering = _render_mode->get_point_rendering(geom_point_rendering);
+  }
+  if (get_tex_gen() != (const TexGenAttrib *)NULL) {
+    geom_point_rendering = _tex_gen->get_point_rendering(geom_point_rendering);
+  }
+
+  return geom_point_rendering;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::set_destructing
 //     Function: RenderState::set_destructing
 //       Access: Private
 //       Access: Private

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

@@ -1575,6 +1575,21 @@ determine_tex_gen() {
   _flags |= F_checked_tex_gen;
   _flags |= F_checked_tex_gen;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::determine_render_mode
+//       Access: Private
+//  Description: This is the private implementation of get_render_mode().
+////////////////////////////////////////////////////////////////////
+void RenderState::
+determine_render_mode() {
+  const RenderAttrib *attrib = get_attrib(RenderModeAttrib::get_class_type());
+  _render_mode = (const RenderModeAttrib *)NULL;
+  if (attrib != (const RenderAttrib *)NULL) {
+    _render_mode = DCAST(RenderModeAttrib, attrib);
+  }
+  _flags |= F_checked_render_mode;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::register_with_read_factory
 //     Function: RenderState::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

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

@@ -27,6 +27,8 @@
 #include "ordered_vector.h"
 #include "ordered_vector.h"
 #include "updateSeq.h"
 #include "updateSeq.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
+#include "renderModeAttrib.h"
+#include "texGenAttrib.h"
 
 
 class GraphicsStateGuardianBase;
 class GraphicsStateGuardianBase;
 class FogAttrib;
 class FogAttrib;
@@ -36,6 +38,7 @@ class ColorAttrib;
 class ColorScaleAttrib;
 class ColorScaleAttrib;
 class TextureAttrib;
 class TextureAttrib;
 class TexGenAttrib;
 class TexGenAttrib;
+class RenderModeAttrib;
 class FactoryParams;
 class FactoryParams;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -122,6 +125,9 @@ PUBLISHED:
   INLINE const ColorScaleAttrib *get_color_scale() const;
   INLINE const ColorScaleAttrib *get_color_scale() const;
   INLINE const TextureAttrib *get_texture() const;
   INLINE const TextureAttrib *get_texture() const;
   INLINE const TexGenAttrib *get_tex_gen() const;
   INLINE const TexGenAttrib *get_tex_gen() const;
+  INLINE const RenderModeAttrib *get_render_mode() const;
+
+  INLINE int get_point_rendering(int geom_point_rendering) const;
 
 
 public:
 public:
   CPT(RenderState) issue_delta_modify(const RenderState *other, 
   CPT(RenderState) issue_delta_modify(const RenderState *other, 
@@ -162,6 +168,7 @@ private:
   void determine_color_scale();
   void determine_color_scale();
   void determine_texture();
   void determine_texture();
   void determine_tex_gen();
   void determine_tex_gen();
+  void determine_render_mode();
 
 
   INLINE void set_destructing();
   INLINE void set_destructing();
   INLINE bool is_destructing() const;
   INLINE bool is_destructing() const;
@@ -239,6 +246,7 @@ private:
   const ColorScaleAttrib *_color_scale;
   const ColorScaleAttrib *_color_scale;
   const TextureAttrib *_texture;
   const TextureAttrib *_texture;
   const TexGenAttrib *_tex_gen;
   const TexGenAttrib *_tex_gen;
+  const RenderModeAttrib *_render_mode;
 
 
   enum Flags {
   enum Flags {
     F_checked_bin_index    = 0x0001,
     F_checked_bin_index    = 0x0001,
@@ -249,6 +257,7 @@ private:
     F_checked_color_scale  = 0x0020,
     F_checked_color_scale  = 0x0020,
     F_checked_texture      = 0x0040,
     F_checked_texture      = 0x0040,
     F_checked_tex_gen      = 0x0080,
     F_checked_tex_gen      = 0x0080,
+    F_checked_render_mode  = 0x0100,
     F_is_destructing       = 0x8000,
     F_is_destructing       = 0x8000,
   };
   };
   unsigned short _flags;
   unsigned short _flags;

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

@@ -24,6 +24,9 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE SceneSetup::
 INLINE SceneSetup::
 SceneSetup() {
 SceneSetup() {
+  _display_region = NULL;
+  _viewport_width = 0;
+  _viewport_height = 0;
   _inverted = false;
   _inverted = false;
   _initial_state = RenderState::make_empty();
   _initial_state = RenderState::make_empty();
   _camera_transform = TransformState::make_identity();
   _camera_transform = TransformState::make_identity();
@@ -31,6 +34,60 @@ SceneSetup() {
   _cs_transform = TransformState::make_identity();
   _cs_transform = TransformState::make_identity();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::set_display_region
+//       Access: Public
+//  Description: Specifies the display region for the scene.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneSetup::
+set_display_region(DisplayRegion *display_region) {
+  _display_region = display_region;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_display_region
+//       Access: Public
+//  Description: Returns the display region for the scene.
+////////////////////////////////////////////////////////////////////
+INLINE DisplayRegion *SceneSetup::
+get_display_region() const {
+  return _display_region;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::set_viewport_size
+//       Access: Public
+//  Description: Specifies the size of the viewport (display region),
+//               in pixels.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneSetup::
+set_viewport_size(int width, int height) {
+  _viewport_width = width;
+  _viewport_height = height;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_viewport_width
+//       Access: Public
+//  Description: Returns the width of the viewport (display region) in
+//               pixels.
+////////////////////////////////////////////////////////////////////
+INLINE int SceneSetup::
+get_viewport_width() const {
+  return _viewport_width;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneSetup::get_viewport_height
+//       Access: Public
+//  Description: Returns the height of the viewport (display region) in
+//               pixels.
+////////////////////////////////////////////////////////////////////
+INLINE int SceneSetup::
+get_viewport_height() const {
+  return _viewport_height;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_scene_root
 //     Function: SceneSetup::set_scene_root
 //       Access: Public
 //       Access: Public

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

@@ -38,6 +38,13 @@ class EXPCL_PANDA SceneSetup : public ReferenceCount {
 public:
 public:
   INLINE SceneSetup();
   INLINE SceneSetup();
 
 
+  INLINE void set_display_region(DisplayRegion *display_region);
+  INLINE DisplayRegion *get_display_region() const;
+
+  INLINE void set_viewport_size(int width, int height);
+  INLINE int get_viewport_width() const;
+  INLINE int get_viewport_height() const;
+
   INLINE void set_scene_root(const NodePath &scene_root);
   INLINE void set_scene_root(const NodePath &scene_root);
   INLINE const NodePath &get_scene_root() const;
   INLINE const NodePath &get_scene_root() const;
 
 
@@ -70,6 +77,9 @@ public:
   INLINE const TransformState *get_render_transform() const;
   INLINE const TransformState *get_render_transform() const;
 
 
 private:
 private:
+  DisplayRegion *_display_region;
+  int _viewport_width;
+  int _viewport_height;
   NodePath _scene_root;
   NodePath _scene_root;
   NodePath _camera_path;
   NodePath _camera_path;
   PT(Camera) _camera_node;
   PT(Camera) _camera_node;

+ 24 - 2
panda/src/pgraph/texGenAttrib.I

@@ -24,7 +24,9 @@
 //               TexGenAttrib object.
 //               TexGenAttrib object.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE TexGenAttrib::
 INLINE TexGenAttrib::
-TexGenAttrib() {
+TexGenAttrib() :
+  _num_point_sprites(0)
+{
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -36,10 +38,30 @@ TexGenAttrib() {
 INLINE TexGenAttrib::
 INLINE TexGenAttrib::
 TexGenAttrib(const TexGenAttrib &copy) :
 TexGenAttrib(const TexGenAttrib &copy) :
   _stages(copy._stages),
   _stages(copy._stages),
-  _no_texcoords(copy._no_texcoords)
+  _no_texcoords(copy._no_texcoords),
+  _num_point_sprites(copy._num_point_sprites)
 {
 {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TexGenAttrib::get_point_rendering
+//       Access: Published
+//  Description: Returns the union of the Geom::PointRendering bits
+//               that will be required once this TexGenAttrib is
+//               applied to a geom which includes the indicated
+//               geom_point_rendering bits.
+////////////////////////////////////////////////////////////////////
+INLINE int TexGenAttrib::
+get_point_rendering(int geom_point_rendering) const {
+  if ((geom_point_rendering & qpGeom::PR_point) != 0) {
+    if (_num_point_sprites > 0) {
+      return geom_point_rendering |= qpGeom::PR_sprite;
+    }
+  }
+
+  return geom_point_rendering;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TexGenAttrib::get_no_texcoords
 //     Function: TexGenAttrib::get_no_texcoords
 //       Access: Public
 //       Access: Public

+ 29 - 0
panda/src/pgraph/texGenAttrib.cxx

@@ -23,6 +23,7 @@
 #include "bamWriter.h"
 #include "bamWriter.h"
 #include "datagram.h"
 #include "datagram.h"
 #include "datagramIterator.h"
 #include "datagramIterator.h"
+#include "dcast.h"
 
 
 CPT(RenderAttrib) TexGenAttrib::_empty_attrib;
 CPT(RenderAttrib) TexGenAttrib::_empty_attrib;
 TypeHandle TexGenAttrib::_type_handle;
 TypeHandle TexGenAttrib::_type_handle;
@@ -53,6 +54,17 @@ make() {
   return _empty_attrib;
   return _empty_attrib;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TexGenAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a TexGenAttrib that generates just the
+//               indicated stage.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) TexGenAttrib::
+make(TextureStage *stage, TexGenAttrib::Mode mode) {
+  return DCAST(TexGenAttrib, make())->add_stage(stage, mode);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TexGenAttrib::add_stage
 //     Function: TexGenAttrib::add_stage
 //       Access: Published, Static
 //       Access: Published, Static
@@ -67,6 +79,9 @@ add_stage(TextureStage *stage, TexGenAttrib::Mode mode) const {
   attrib->_stages[stage] = mode;
   attrib->_stages[stage] = mode;
   if (mode != M_off) {
   if (mode != M_off) {
     attrib->_no_texcoords.insert(stage);
     attrib->_no_texcoords.insert(stage);
+    if (mode == M_point_sprite) {
+      attrib->_num_point_sprites++;
+    }
   }
   }
   return return_new(attrib);
   return return_new(attrib);
 }
 }
@@ -79,9 +94,19 @@ add_stage(TextureStage *stage, TexGenAttrib::Mode mode) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) TexGenAttrib::
 CPT(RenderAttrib) TexGenAttrib::
 remove_stage(TextureStage *stage) const {
 remove_stage(TextureStage *stage) const {
+  Stages::const_iterator si;
+  si = _stages.find(stage);
+  if (si == _stages.end()) {
+    return this;
+  }
+
+  Mode mode = (*si).second;
   TexGenAttrib *attrib = new TexGenAttrib(*this);
   TexGenAttrib *attrib = new TexGenAttrib(*this);
   attrib->_stages.erase(stage);
   attrib->_stages.erase(stage);
   attrib->_no_texcoords.erase(stage);
   attrib->_no_texcoords.erase(stage);
+  if (mode == M_point_sprite) {
+    attrib->_num_point_sprites--;
+  }
   return return_new(attrib);
   return return_new(attrib);
 }
 }
 
 
@@ -185,6 +210,10 @@ output(ostream &out) const {
     case M_eye_position:
     case M_eye_position:
       out << "eye_position";
       out << "eye_position";
       break;
       break;
+
+    case M_point_sprite:
+      out << "point_sprite";
+      break;
     }
     }
     out << ")";
     out << ")";
   }
   }

+ 13 - 0
panda/src/pgraph/texGenAttrib.h

@@ -26,6 +26,7 @@
 #include "textureStage.h"
 #include "textureStage.h"
 #include "texture.h"
 #include "texture.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
+#include "qpgeom.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : TexGenAttrib
 //       Class : TexGenAttrib
@@ -76,6 +77,13 @@ PUBLISHED:
     M_world_position,
     M_world_position,
     M_object_position,
     M_object_position,
     M_eye_position,
     M_eye_position,
+
+    // With M_point_sprite, texture coordinates will be generated for
+    // large points in the range (0,0) - (1,1) from upper-left to
+    // lower-right across the point's face.  Without this, each point
+    // will have just a single uniform texture coordinate value across
+    // its face.
+    M_point_sprite,
   };
   };
 
 
 protected:
 protected:
@@ -87,6 +95,7 @@ public:
 
 
 PUBLISHED:
 PUBLISHED:
   static CPT(RenderAttrib) make();
   static CPT(RenderAttrib) make();
+  static CPT(RenderAttrib) make(TextureStage *stage, Mode mode);
 
 
   CPT(RenderAttrib) add_stage(TextureStage *stage, Mode mode) const;
   CPT(RenderAttrib) add_stage(TextureStage *stage, Mode mode) const;
   CPT(RenderAttrib) remove_stage(TextureStage *stage) const;
   CPT(RenderAttrib) remove_stage(TextureStage *stage) const;
@@ -95,6 +104,8 @@ PUBLISHED:
   bool has_stage(TextureStage *stage) const;
   bool has_stage(TextureStage *stage) const;
   Mode get_mode(TextureStage *stage) const;
   Mode get_mode(TextureStage *stage) const;
 
 
+  INLINE int get_point_rendering(int geom_point_rendering) const;
+
 public:
 public:
   INLINE const Geom::NoTexCoordStages &get_no_texcoords() const;
   INLINE const Geom::NoTexCoordStages &get_no_texcoords() const;
 
 
@@ -122,6 +133,8 @@ private:
   // no meaningful value any other time.
   // no meaningful value any other time.
   pvector<Mode> _read_modes;
   pvector<Mode> _read_modes;
 
 
+  int _num_point_sprites;
+  
   static CPT(RenderAttrib) _empty_attrib;
   static CPT(RenderAttrib) _empty_attrib;
 
 
 public:
 public:

+ 1 - 0
panda/src/pstatclient/pStatProperties.cxx

@@ -127,6 +127,7 @@ static TimeCollectorProperties time_properties[] = {
   { 1, "Cull:Show fps",                    { 0.5, 0.8, 1.0 } },
   { 1, "Cull:Show fps",                    { 0.5, 0.8, 1.0 } },
   { 1, "Cull:Bins",                        { 0.3, 0.6, 0.3 } },
   { 1, "Cull:Bins",                        { 0.3, 0.6, 0.3 } },
   { 1, "Cull:Munge",                       { 0.3, 0.3, 0.9 } },
   { 1, "Cull:Munge",                       { 0.3, 0.3, 0.9 } },
+  { 1, "Cull:Munge:Points",                { 0.2, 0.8, 0.4 } },
   { 1, "Cull:Munge:Data",                  { 0.7, 0.5, 0.2 } },
   { 1, "Cull:Munge:Data",                  { 0.7, 0.5, 0.2 } },
   { 1, "Draw",                             { 1.0, 0.0, 0.0 },  1.0 / 30.0 },
   { 1, "Draw",                             { 1.0, 0.0, 0.0 },  1.0 / 30.0 },
   { 1, "Draw:Make current",                { 0.4, 0.2, 0.6 } },
   { 1, "Draw:Make current",                { 0.4, 0.2, 0.6 } },