David Rose 20 роки тому
батько
коміт
01bd239ce7

+ 4 - 1
panda/src/glstuff/Sources.pp

@@ -13,7 +13,7 @@
 
 
     
     
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
-     glext.h \
+     panda_glext.h \
      glmisc_src.cxx \
      glmisc_src.cxx \
      glmisc_src.h \
      glmisc_src.h \
      glstuff_src.cxx \
      glstuff_src.cxx \
@@ -22,6 +22,9 @@
      glVertexBufferContext_src.cxx \
      glVertexBufferContext_src.cxx \
      glVertexBufferContext_src.I \
      glVertexBufferContext_src.I \
      glVertexBufferContext_src.h \
      glVertexBufferContext_src.h \
+     glImmediateModeSender_src.cxx \
+     glImmediateModeSender_src.I \
+     glImmediateModeSender_src.h \
      glIndexBufferContext_src.cxx \
      glIndexBufferContext_src.cxx \
      glIndexBufferContext_src.I \
      glIndexBufferContext_src.I \
      glIndexBufferContext_src.h \
      glIndexBufferContext_src.h \

+ 403 - 217
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -75,6 +75,7 @@ TypeHandle CLP(GraphicsStateGuardian)::_type_handle;
 PStatCollector CLP(GraphicsStateGuardian)::_load_display_list_pcollector("Draw:Transfer data:Display lists");
 PStatCollector CLP(GraphicsStateGuardian)::_load_display_list_pcollector("Draw:Transfer data:Display lists");
 PStatCollector CLP(GraphicsStateGuardian)::_primitive_batches_display_list_pcollector("Primitive batches:Display lists");
 PStatCollector CLP(GraphicsStateGuardian)::_primitive_batches_display_list_pcollector("Primitive batches:Display lists");
 PStatCollector CLP(GraphicsStateGuardian)::_vertices_display_list_pcollector("Vertices:Display lists");
 PStatCollector CLP(GraphicsStateGuardian)::_vertices_display_list_pcollector("Vertices:Display lists");
+PStatCollector CLP(GraphicsStateGuardian)::_vertices_immediate_pcollector("Vertices:Immediate mode");
 
 
 // The following noop functions are assigned to the corresponding
 // The following noop functions are assigned to the corresponding
 // glext function pointers in the class, in case the functions are not
 // glext function pointers in the class, in case the functions are not
@@ -108,6 +109,7 @@ static void APIENTRY
 null_glBlendColor(GLclampf, GLclampf, GLclampf, GLclampf) {
 null_glBlendColor(GLclampf, GLclampf, GLclampf, GLclampf) {
 }
 }
 
 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: uchar_bgr_to_rgb
 //     Function: uchar_bgr_to_rgb
 //  Description: Recopies the given array of pixels, converting from
 //  Description: Recopies the given array of pixels, converting from
@@ -489,8 +491,14 @@ reset() {
       get_extension_func(GLPREFIX_QUOTED, "ActiveTexture");
       get_extension_func(GLPREFIX_QUOTED, "ActiveTexture");
     _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
     _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
       get_extension_func(GLPREFIX_QUOTED, "ClientActiveTexture");
       get_extension_func(GLPREFIX_QUOTED, "ClientActiveTexture");
-    _glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)
-      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2fv");
+    _glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC)
+      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord1f");
+    _glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC)
+      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2f");
+    _glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC)
+      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord3f");
+    _glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC)
+      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord4f");
 
 
   } else if (has_extension("GL_ARB_multitexture")) {
   } else if (has_extension("GL_ARB_multitexture")) {
     _supports_multitexture = true;
     _supports_multitexture = true;
@@ -499,13 +507,20 @@ reset() {
       get_extension_func(GLPREFIX_QUOTED, "ActiveTextureARB");
       get_extension_func(GLPREFIX_QUOTED, "ActiveTextureARB");
     _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
     _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
       get_extension_func(GLPREFIX_QUOTED, "ClientActiveTextureARB");
       get_extension_func(GLPREFIX_QUOTED, "ClientActiveTextureARB");
-    _glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)
-      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2fvARB");
+    _glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC)
+      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord1fARB");
+    _glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC)
+      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2fARB");
+    _glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC)
+      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord3fARB");
+    _glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC)
+      get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord4fARB");
   }
   }
 
 
   if (_supports_multitexture) {
   if (_supports_multitexture) {
     if (_glActiveTexture == NULL || _glClientActiveTexture == NULL ||
     if (_glActiveTexture == NULL || _glClientActiveTexture == NULL ||
-        _glMultiTexCoord2fv == NULL) {
+        _glMultiTexCoord1f == NULL || _glMultiTexCoord2f == NULL || 
+        _glMultiTexCoord3f == NULL || _glMultiTexCoord4f == NULL) {
       GLCAT.warning()
       GLCAT.warning()
         << "Multitexture advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
         << "Multitexture advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
       _supports_multitexture = false;
       _supports_multitexture = false;
@@ -687,6 +702,11 @@ reset() {
       GLCAT.debug()
       GLCAT.debug()
         << "vertex buffer objects are NOT supported.\n";
         << "vertex buffer objects are NOT supported.\n";
     }
     }
+
+    if (!vertex_arrays) {
+      GLCAT.debug()
+        << "immediate mode commands will be used instead of vertex arrays.\n";
+    }
   }
   }
 
 
   report_my_gl_errors();
   report_my_gl_errors();
@@ -1270,125 +1290,192 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
 #endif
 #endif
   }
   }
 
 
-  const GeomVertexArrayData *array_data;
-  int num_values;
-  Geom::NumericType numeric_type;
-  int start;
-  int stride;
+  if (!vertex_arrays) {
+    // We must use immediate mode to render primitives.
+    _sender.clear();
+    _use_sender = true;
 
 
-  if (_vertex_data->get_vertex_info(array_data, num_values, numeric_type, 
-                                    start, stride)) {
-    const unsigned char *client_pointer = setup_array_data(array_data);
-    GLP(VertexPointer)(num_values, get_numeric_type(numeric_type), 
-                       stride, client_pointer + start);
-    GLP(EnableClientState)(GL_VERTEX_ARRAY);
-  }
-
-  if (_vertex_data->get_normal_info(array_data, numeric_type, 
-                                    start, stride)) {
-    const unsigned char *client_pointer = setup_array_data(array_data);
-    GLP(NormalPointer)(get_numeric_type(numeric_type), stride, 
-                       client_pointer + start);
-    GLP(EnableClientState)(GL_NORMAL_ARRAY);
-  } else {
-    GLP(DisableClientState)(GL_NORMAL_ARRAY);
-  }
+    _sender.add_column(_vertex_data, InternalName::get_normal(),
+                       NULL, NULL, GLP(Normal3f), NULL);
+    if (!_sender.add_column(_vertex_data, InternalName::get_color(),
+                            NULL, NULL, GLP(Color3f), GLP(Color4f))) {
+      // If we didn't have a color column, the item color is white.
+      GLP(Color4f)(1.0f, 1.0f, 1.0f, 1.0f);
+    }
 
 
-  if (_vertex_data->get_color_info(array_data, num_values, numeric_type, 
-                                   start, stride) &&
-      numeric_type != Geom::NT_packed_dabc) {
-    const unsigned char *client_pointer = setup_array_data(array_data);
-    GLP(ColorPointer)(num_values, get_numeric_type(numeric_type), 
-                      stride, client_pointer + start);
-    GLP(EnableClientState)(GL_COLOR_ARRAY);
-  } else {
-    GLP(DisableClientState)(GL_COLOR_ARRAY);
-
-    // Since we don't have per-vertex color, the implicit color is
-    // white.
-    GLP(Color4f)(1.0f, 1.0f, 1.0f, 1.0f);
-  }
-
-  // Now set up each of the active texture coordinate stages--or at
-  // least those for which we're not generating texture coordinates
-  // automatically.
-  const Geom::ActiveTextureStages &active_stages = 
-    _current_texture->get_on_stages();
-  const Geom::NoTexCoordStages &no_texcoords = 
-    _current_tex_gen->get_no_texcoords();
-
-  int max_stage_index = (int)active_stages.size();
-  int stage_index = 0;
-  while (stage_index < max_stage_index) {
-    _glClientActiveTexture(GL_TEXTURE0 + stage_index);
-    TextureStage *stage = active_stages[stage_index];
-    if (no_texcoords.find(stage) == no_texcoords.end()) {
-      // This stage is not one of the stages that doesn't need
-      // texcoords issued for it.
-      const InternalName *name = stage->get_texcoord_name();
-
-      if (_vertex_data->get_array_info(name, array_data, num_values, 
-                                       numeric_type, start, stride)) {
-        // The vertex data does have texcoords for this stage.
-        const unsigned char *client_pointer = setup_array_data(array_data);
-        GLP(TexCoordPointer)(num_values, get_numeric_type(numeric_type), 
-                             stride, client_pointer + start);
-        GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY);
+    // Now set up each of the active texture coordinate stages--or at
+    // least those for which we're not generating texture coordinates
+    // automatically.
+    const Geom::ActiveTextureStages &active_stages = 
+      _current_texture->get_on_stages();
+    const Geom::NoTexCoordStages &no_texcoords = 
+      _current_tex_gen->get_no_texcoords();
+    
+    int max_stage_index = (int)active_stages.size();
+    int stage_index = 0;
+    while (stage_index < max_stage_index) {
+      TextureStage *stage = active_stages[stage_index];
+      if (no_texcoords.find(stage) == no_texcoords.end()) {
+        // This stage is not one of the stages that doesn't need
+        // texcoords issued for it.
+        const InternalName *name = stage->get_texcoord_name();
+        if (stage_index == 0) {
+          // Use the original functions for stage 0, in case we don't
+          // support multitexture.
+          _sender.add_column(_vertex_data, name,
+                             GLP(TexCoord1f), GLP(TexCoord2f),
+                             GLP(TexCoord3f), GLP(TexCoord4f));
 
 
-      } else {
-        // The vertex data doesn't have texcoords for this stage (even
-        // though they're needed).
-        GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
+        } else {
+          // Other stages require the multitexture functions.
+          _sender.add_texcoord_column(_vertex_data, name, stage_index,
+                                      _glMultiTexCoord1f, _glMultiTexCoord2f, 
+                                      _glMultiTexCoord3f, _glMultiTexCoord4f);
+        }
       }
       }
-    } else {
-      // No texcoords are needed for this stage.
+      
+      ++stage_index;
+    }
+    
+    // Be sure also to disable any texture stages we had enabled before.
+    while (stage_index < _last_max_stage_index) {
+      _glClientActiveTexture(GL_TEXTURE0 + stage_index);
       GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
       GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
+      ++stage_index;
     }
     }
+    _last_max_stage_index = max_stage_index;
 
 
-    ++stage_index;
-  }
-
-  // Be sure also to disable any texture stages we had enabled before.
-  while (stage_index < _last_max_stage_index) {
-    _glClientActiveTexture(GL_TEXTURE0 + stage_index);
-    GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
-    ++stage_index;
-  }
-  _last_max_stage_index = max_stage_index;
+    // We must add vertex last, because glVertex3f() is the key
+    // function call that actually issues the vertex.
+    _sender.add_column(_vertex_data, InternalName::get_vertex(),
+                       NULL, GLP(Vertex2f), GLP(Vertex3f), GLP(Vertex4f));
 
 
-  if (_supports_vertex_blend) {
-    if (hardware_animation) {
-      // Issue the weights and/or transform indices for vertex blending.
-      if (_vertex_data->get_array_info(InternalName::get_transform_weight(),
-                                       array_data, num_values, numeric_type, 
-                                       start, stride)) {
-        const unsigned char *client_pointer = setup_array_data(array_data);
-        _glWeightPointerARB(num_values, get_numeric_type(numeric_type), 
-                            stride, client_pointer + start);
-        GLP(EnableClientState)(GL_WEIGHT_ARRAY_ARB);
+  } else {
+    // We may use vertex arrays or buffers to render primitives.
+    _use_sender = false;
+
+    const GeomVertexArrayData *array_data;
+    int num_values;
+    Geom::NumericType numeric_type;
+    int start;
+    int stride;
+    
+    if (_vertex_data->get_normal_info(array_data, numeric_type, 
+                                      start, stride)) {
+      const unsigned char *client_pointer = setup_array_data(array_data);
+      GLP(NormalPointer)(get_numeric_type(numeric_type), stride, 
+                         client_pointer + start);
+      GLP(EnableClientState)(GL_NORMAL_ARRAY);
+    } else {
+      GLP(DisableClientState)(GL_NORMAL_ARRAY);
+    }
+    
+    if (_vertex_data->get_color_info(array_data, num_values, numeric_type, 
+                                     start, stride) &&
+        numeric_type != Geom::NT_packed_dabc) {
+      const unsigned char *client_pointer = setup_array_data(array_data);
+      GLP(ColorPointer)(num_values, get_numeric_type(numeric_type), 
+                        stride, client_pointer + start);
+      GLP(EnableClientState)(GL_COLOR_ARRAY);
+    } else {
+      GLP(DisableClientState)(GL_COLOR_ARRAY);
+      
+      // Since we don't have per-vertex color, the implicit color is
+      // white.
+      GLP(Color4f)(1.0f, 1.0f, 1.0f, 1.0f);
+    }
+    
+    // Now set up each of the active texture coordinate stages--or at
+    // least those for which we're not generating texture coordinates
+    // automatically.
+    const Geom::ActiveTextureStages &active_stages = 
+      _current_texture->get_on_stages();
+    const Geom::NoTexCoordStages &no_texcoords = 
+      _current_tex_gen->get_no_texcoords();
+    
+    int max_stage_index = (int)active_stages.size();
+    int stage_index = 0;
+    while (stage_index < max_stage_index) {
+      _glClientActiveTexture(GL_TEXTURE0 + stage_index);
+      TextureStage *stage = active_stages[stage_index];
+      if (no_texcoords.find(stage) == no_texcoords.end()) {
+        // This stage is not one of the stages that doesn't need
+        // texcoords issued for it.
+        const InternalName *name = stage->get_texcoord_name();
+        
+        if (_vertex_data->get_array_info(name, array_data, num_values, 
+                                         numeric_type, start, stride)) {
+          // The vertex data does have texcoords for this stage.
+          const unsigned char *client_pointer = setup_array_data(array_data);
+          GLP(TexCoordPointer)(num_values, get_numeric_type(numeric_type), 
+                               stride, client_pointer + start);
+          GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY);
+          
+        } else {
+          // The vertex data doesn't have texcoords for this stage (even
+          // though they're needed).
+          GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
+        }
       } else {
       } else {
-        GLP(DisableClientState)(GL_WEIGHT_ARRAY_ARB);
+        // No texcoords are needed for this stage.
+        GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
       }
       }
+      
+      ++stage_index;
+    }
+    
+    // Be sure also to disable any texture stages we had enabled before.
+    while (stage_index < _last_max_stage_index) {
+      _glClientActiveTexture(GL_TEXTURE0 + stage_index);
+      GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
+      ++stage_index;
+    }
+    _last_max_stage_index = max_stage_index;
 
 
-      if (animation.get_indexed_transforms()) {
-        // Issue the matrix palette indices.
-        if (_vertex_data->get_array_info(InternalName::get_transform_index(),
+    if (_supports_vertex_blend) {
+      if (hardware_animation) {
+        // Issue the weights and/or transform indices for vertex blending.
+        if (_vertex_data->get_array_info(InternalName::get_transform_weight(),
                                          array_data, num_values, numeric_type, 
                                          array_data, num_values, numeric_type, 
                                          start, stride)) {
                                          start, stride)) {
           const unsigned char *client_pointer = setup_array_data(array_data);
           const unsigned char *client_pointer = setup_array_data(array_data);
-          _glMatrixIndexPointerARB(num_values, get_numeric_type(numeric_type), 
+          _glWeightPointerARB(num_values, get_numeric_type(numeric_type), 
                               stride, client_pointer + start);
                               stride, client_pointer + start);
-          GLP(EnableClientState)(GL_MATRIX_INDEX_ARRAY_ARB);
+          GLP(EnableClientState)(GL_WEIGHT_ARRAY_ARB);
         } else {
         } else {
+          GLP(DisableClientState)(GL_WEIGHT_ARRAY_ARB);
+        }
+        
+        if (animation.get_indexed_transforms()) {
+          // Issue the matrix palette indices.
+          if (_vertex_data->get_array_info(InternalName::get_transform_index(),
+                                           array_data, num_values, numeric_type, 
+                                           start, stride)) {
+            const unsigned char *client_pointer = setup_array_data(array_data);
+            _glMatrixIndexPointerARB(num_values, get_numeric_type(numeric_type), 
+                                     stride, client_pointer + start);
+            GLP(EnableClientState)(GL_MATRIX_INDEX_ARRAY_ARB);
+          } else {
+            GLP(DisableClientState)(GL_MATRIX_INDEX_ARRAY_ARB);
+          }
+        }
+        
+      } else {
+        GLP(DisableClientState)(GL_WEIGHT_ARRAY_ARB);
+        if (_supports_matrix_palette) {
           GLP(DisableClientState)(GL_MATRIX_INDEX_ARRAY_ARB);
           GLP(DisableClientState)(GL_MATRIX_INDEX_ARRAY_ARB);
         }
         }
       }
       }
+    }
 
 
-    } else {
-      GLP(DisableClientState)(GL_WEIGHT_ARRAY_ARB);
-      if (_supports_matrix_palette) {
-        GLP(DisableClientState)(GL_MATRIX_INDEX_ARRAY_ARB);
-      }
+    // There's no requirement that we add vertices last, but we do
+    // anyway.
+    if (_vertex_data->get_vertex_info(array_data, num_values, numeric_type, 
+                                      start, stride)) {
+      const unsigned char *client_pointer = setup_array_data(array_data);
+      GLP(VertexPointer)(num_values, get_numeric_type(numeric_type), 
+                         stride, client_pointer + start);
+      GLP(EnableClientState)(GL_VERTEX_ARRAY);
     }
     }
   }
   }
 
 
@@ -1412,21 +1499,26 @@ draw_triangles(const GeomTriangles *primitive) {
   _vertices_tri_pcollector.add_level(primitive->get_num_vertices());
   _vertices_tri_pcollector.add_level(primitive->get_num_vertices());
   _primitive_batches_tri_pcollector.add_level(1);
   _primitive_batches_tri_pcollector.add_level(1);
 
 
-  if (primitive->is_indexed()) {
-    const unsigned char *client_pointer = setup_primitive(primitive);
-    
-    _glDrawRangeElements(GL_TRIANGLES, 
-                         primitive->get_min_vertex(),
-                         primitive->get_max_vertex(),
-                         primitive->get_num_vertices(),
-                         get_numeric_type(primitive->get_index_type()), 
-                         client_pointer);
+  if (_use_sender) {
+    draw_immediate_simple_primitives(primitive, GL_TRIANGLES);
+
   } else {
   } else {
-    GLP(DrawArrays)(GL_TRIANGLES,
-                    primitive->get_first_vertex(),
-                    primitive->get_num_vertices());
+    if (primitive->is_indexed()) {
+      const unsigned char *client_pointer = setup_primitive(primitive);
+      
+      _glDrawRangeElements(GL_TRIANGLES, 
+                           primitive->get_min_vertex(),
+                           primitive->get_max_vertex(),
+                           primitive->get_num_vertices(),
+                           get_numeric_type(primitive->get_index_type()), 
+                           client_pointer);
+    } else {
+      GLP(DrawArrays)(GL_TRIANGLES,
+                      primitive->get_first_vertex(),
+                      primitive->get_num_vertices());
+    }
   }
   }
-
+    
   report_my_gl_errors();
   report_my_gl_errors();
 }
 }
 
 
@@ -1443,57 +1535,62 @@ draw_tristrips(const GeomTristrips *primitive) {
   }
   }
 #endif  // NDEBUG
 #endif  // NDEBUG
 
 
-  if (connect_triangle_strips && _render_mode != RenderModeAttrib::M_wireframe) {
-    // One long triangle strip, connected by the degenerate vertices
-    // that have already been set up within the primitive.
-    _vertices_tristrip_pcollector.add_level(primitive->get_num_vertices());
-    _primitive_batches_tristrip_pcollector.add_level(1);
-    if (primitive->is_indexed()) {
-      const unsigned char *client_pointer = setup_primitive(primitive);
-      _glDrawRangeElements(GL_TRIANGLE_STRIP, 
-                           primitive->get_min_vertex(),
-                           primitive->get_max_vertex(),
-                           primitive->get_num_vertices(),
-                           get_numeric_type(primitive->get_index_type()), 
-                           client_pointer);
-    } else {
-      GLP(DrawArrays)(GL_TRIANGLE_STRIP,
-                      primitive->get_first_vertex(),
-                      primitive->get_num_vertices());
-    }
+  if (_use_sender) {
+    draw_immediate_composite_primitives(primitive, GL_TRIANGLE_STRIP);
 
 
   } else {
   } else {
-    // Send the individual triangle strips, stepping over the
-    // degenerate vertices.
-    CPTA_int ends = primitive->get_ends();
-    
-    _primitive_batches_tristrip_pcollector.add_level(ends.size());
-    if (primitive->is_indexed()) {
-      const unsigned char *client_pointer = setup_primitive(primitive);
-      int index_stride = primitive->get_index_stride();
-      GeomVertexReader mins(primitive->get_mins(), 0);
-      GeomVertexReader maxs(primitive->get_maxs(), 0);
-      nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() && 
-               primitive->get_maxs()->get_num_rows() == (int)ends.size());
-
-      unsigned int start = 0;
-      for (size_t i = 0; i < ends.size(); i++) {
-        _vertices_tristrip_pcollector.add_level(ends[i] - start);
+    if (connect_triangle_strips && _render_mode != RenderModeAttrib::M_wireframe) {
+      // One long triangle strip, connected by the degenerate vertices
+      // that have already been set up within the primitive.
+      _vertices_tristrip_pcollector.add_level(primitive->get_num_vertices());
+      _primitive_batches_tristrip_pcollector.add_level(1);
+      if (primitive->is_indexed()) {
+        const unsigned char *client_pointer = setup_primitive(primitive);
         _glDrawRangeElements(GL_TRIANGLE_STRIP, 
         _glDrawRangeElements(GL_TRIANGLE_STRIP, 
-                             mins.get_data1i(), maxs.get_data1i(), 
-                             ends[i] - start,
+                             primitive->get_min_vertex(),
+                             primitive->get_max_vertex(),
+                             primitive->get_num_vertices(),
                              get_numeric_type(primitive->get_index_type()), 
                              get_numeric_type(primitive->get_index_type()), 
-                             client_pointer + start * index_stride);
-        start = ends[i] + 2;
+                             client_pointer);
+      } else {
+        GLP(DrawArrays)(GL_TRIANGLE_STRIP,
+                        primitive->get_first_vertex(),
+                        primitive->get_num_vertices());
       }
       }
+      
     } else {
     } else {
-      unsigned int start = 0;
-      int first_vertex = primitive->get_first_vertex();
-      for (size_t i = 0; i < ends.size(); i++) {
-        _vertices_tristrip_pcollector.add_level(ends[i] - start);
-        GLP(DrawArrays)(GL_TRIANGLE_STRIP, first_vertex + start, 
-                        ends[i] - start);
-        start = ends[i] + 2;
+      // Send the individual triangle strips, stepping over the
+      // degenerate vertices.
+      CPTA_int ends = primitive->get_ends();
+      
+      _primitive_batches_tristrip_pcollector.add_level(ends.size());
+      if (primitive->is_indexed()) {
+        const unsigned char *client_pointer = setup_primitive(primitive);
+        int index_stride = primitive->get_index_stride();
+        GeomVertexReader mins(primitive->get_mins(), 0);
+        GeomVertexReader maxs(primitive->get_maxs(), 0);
+        nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() && 
+                 primitive->get_maxs()->get_num_rows() == (int)ends.size());
+        
+        unsigned int start = 0;
+        for (size_t i = 0; i < ends.size(); i++) {
+          _vertices_tristrip_pcollector.add_level(ends[i] - start);
+          _glDrawRangeElements(GL_TRIANGLE_STRIP, 
+                               mins.get_data1i(), maxs.get_data1i(), 
+                               ends[i] - start,
+                               get_numeric_type(primitive->get_index_type()), 
+                               client_pointer + start * index_stride);
+          start = ends[i] + 2;
+        }
+      } else {
+        unsigned int start = 0;
+        int first_vertex = primitive->get_first_vertex();
+        for (size_t i = 0; i < ends.size(); i++) {
+          _vertices_tristrip_pcollector.add_level(ends[i] - start);
+          GLP(DrawArrays)(GL_TRIANGLE_STRIP, first_vertex + start, 
+                          ends[i] - start);
+          start = ends[i] + 2;
+        }
       }
       }
     }
     }
   }
   }
@@ -1514,36 +1611,41 @@ draw_trifans(const GeomTrifans *primitive) {
   }
   }
 #endif  // NDEBUG
 #endif  // NDEBUG
 
 
-  // Send the individual triangle fans.  There's no connecting fans
-  // with degenerate vertices, so no worries about that.
-  CPTA_int ends = primitive->get_ends();
+  if (_use_sender) {
+    draw_immediate_composite_primitives(primitive, GL_TRIANGLE_FAN);
 
 
-  _primitive_batches_trifan_pcollector.add_level(ends.size());
-  if (primitive->is_indexed()) {
-    const unsigned char *client_pointer = setup_primitive(primitive);
-    int index_stride = primitive->get_index_stride();
-    GeomVertexReader mins(primitive->get_mins(), 0);
-    GeomVertexReader maxs(primitive->get_maxs(), 0);
-    nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() && 
-             primitive->get_maxs()->get_num_rows() == (int)ends.size());
-
-    unsigned int start = 0;
-    for (size_t i = 0; i < ends.size(); i++) {
-      _vertices_trifan_pcollector.add_level(ends[i] - start);
-      _glDrawRangeElements(GL_TRIANGLE_FAN, 
-                           mins.get_data1i(), maxs.get_data1i(), ends[i] - start,
-                           get_numeric_type(primitive->get_index_type()), 
-                           client_pointer + start * index_stride);
-      start = ends[i];
-    }
   } else {
   } else {
-    unsigned int start = 0;
-    int first_vertex = primitive->get_first_vertex();
-    for (size_t i = 0; i < ends.size(); i++) {
-      _vertices_trifan_pcollector.add_level(ends[i] - start);
-      GLP(DrawArrays)(GL_TRIANGLE_FAN, first_vertex + start,
-                      ends[i] - start);
-      start = ends[i];
+    // Send the individual triangle fans.  There's no connecting fans
+    // with degenerate vertices, so no worries about that.
+    CPTA_int ends = primitive->get_ends();
+    
+    _primitive_batches_trifan_pcollector.add_level(ends.size());
+    if (primitive->is_indexed()) {
+      const unsigned char *client_pointer = setup_primitive(primitive);
+      int index_stride = primitive->get_index_stride();
+      GeomVertexReader mins(primitive->get_mins(), 0);
+      GeomVertexReader maxs(primitive->get_maxs(), 0);
+      nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() && 
+               primitive->get_maxs()->get_num_rows() == (int)ends.size());
+      
+      unsigned int start = 0;
+      for (size_t i = 0; i < ends.size(); i++) {
+        _vertices_trifan_pcollector.add_level(ends[i] - start);
+        _glDrawRangeElements(GL_TRIANGLE_FAN, 
+                             mins.get_data1i(), maxs.get_data1i(), ends[i] - start,
+                             get_numeric_type(primitive->get_index_type()), 
+                             client_pointer + start * index_stride);
+        start = ends[i];
+      }
+    } else {
+      unsigned int start = 0;
+      int first_vertex = primitive->get_first_vertex();
+      for (size_t i = 0; i < ends.size(); i++) {
+        _vertices_trifan_pcollector.add_level(ends[i] - start);
+        GLP(DrawArrays)(GL_TRIANGLE_FAN, first_vertex + start,
+                        ends[i] - start);
+        start = ends[i];
+      }
     }
     }
   }
   }
     
     
@@ -1563,21 +1665,26 @@ draw_lines(const GeomLines *primitive) {
   }
   }
 #endif  // NDEBUG
 #endif  // NDEBUG
 
 
-  _vertices_other_pcollector.add_level(primitive->get_num_vertices());
-  _primitive_batches_other_pcollector.add_level(1);
+  if (_use_sender) {
+    draw_immediate_simple_primitives(primitive, GL_LINES);
 
 
-  if (primitive->is_indexed()) {
-    const unsigned char *client_pointer = setup_primitive(primitive);
-    _glDrawRangeElements(GL_LINES, 
-                         primitive->get_min_vertex(),
-                         primitive->get_max_vertex(),
-                         primitive->get_num_vertices(),
-                         get_numeric_type(primitive->get_index_type()), 
-                         client_pointer);
   } else {
   } else {
-    GLP(DrawArrays)(GL_LINES,
-                    primitive->get_first_vertex(),
-                    primitive->get_num_vertices());
+    _vertices_other_pcollector.add_level(primitive->get_num_vertices());
+    _primitive_batches_other_pcollector.add_level(1);
+    
+    if (primitive->is_indexed()) {
+      const unsigned char *client_pointer = setup_primitive(primitive);
+      _glDrawRangeElements(GL_LINES, 
+                           primitive->get_min_vertex(),
+                           primitive->get_max_vertex(),
+                           primitive->get_num_vertices(),
+                           get_numeric_type(primitive->get_index_type()), 
+                           client_pointer);
+    } else {
+      GLP(DrawArrays)(GL_LINES,
+                      primitive->get_first_vertex(),
+                      primitive->get_num_vertices());
+    }
   }
   }
 
 
   report_my_gl_errors();
   report_my_gl_errors();
@@ -1611,21 +1718,26 @@ draw_points(const GeomPoints *primitive) {
   }
   }
 #endif  // NDEBUG
 #endif  // NDEBUG
 
 
-  _vertices_other_pcollector.add_level(primitive->get_num_vertices());
-  _primitive_batches_other_pcollector.add_level(1);
+  if (_use_sender) {
+    draw_immediate_simple_primitives(primitive, GL_POINTS);
 
 
-  if (primitive->is_indexed()) {
-    const unsigned char *client_pointer = setup_primitive(primitive);
-    _glDrawRangeElements(GL_POINTS, 
-                         primitive->get_min_vertex(),
-                         primitive->get_max_vertex(),
-                         primitive->get_num_vertices(),
-                         get_numeric_type(primitive->get_index_type()), 
-                         client_pointer);
   } else {
   } else {
-    GLP(DrawArrays)(GL_POINTS,
-                    primitive->get_first_vertex(),
-                    primitive->get_num_vertices());
+    _vertices_other_pcollector.add_level(primitive->get_num_vertices());
+    _primitive_batches_other_pcollector.add_level(1);
+    
+    if (primitive->is_indexed()) {
+      const unsigned char *client_pointer = setup_primitive(primitive);
+      _glDrawRangeElements(GL_POINTS, 
+                           primitive->get_min_vertex(),
+                           primitive->get_max_vertex(),
+                           primitive->get_num_vertices(),
+                           get_numeric_type(primitive->get_index_type()), 
+                           client_pointer);
+    } else {
+      GLP(DrawArrays)(GL_POINTS,
+                      primitive->get_first_vertex(),
+                      primitive->get_num_vertices());
+    }
   }
   }
 
 
   report_my_gl_errors();
   report_my_gl_errors();
@@ -2950,7 +3062,81 @@ wants_texcoords() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: report_errors_loop
+//     Function: GLGraphicsStateGuardian::draw_immediate_simple_primitives
+//       Access: Protected
+//  Description: Uses the ImmediateModeSender to draw a series of
+//               primitives of the indicated type.
+////////////////////////////////////////////////////////////////////
+void CLP(GraphicsStateGuardian)::
+draw_immediate_simple_primitives(const GeomPrimitive *primitive, GLenum mode) {
+  _vertices_immediate_pcollector.add_level(primitive->get_num_vertices());
+  GLP(Begin)(mode);
+
+  if (primitive->is_indexed()) {
+    for (int v = 0; v < primitive->get_num_vertices(); ++v) {
+      _sender.set_vertex(primitive->get_vertex(v));
+      _sender.issue_vertex();
+    }
+
+  } else {
+    _sender.set_vertex(primitive->get_first_vertex());
+    for (int v = 0; v < primitive->get_num_vertices(); ++v) {
+      _sender.issue_vertex();
+    }
+  }
+
+  GLP(End)();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::draw_immediate_composite_primitives
+//       Access: Protected
+//  Description: Uses the ImmediateModeSender to draw a series of
+//               primitives of the indicated type.  This form is for
+//               primitive types like tristrips which must involve
+//               several begin/end groups.
+////////////////////////////////////////////////////////////////////
+void CLP(GraphicsStateGuardian)::
+draw_immediate_composite_primitives(const GeomPrimitive *primitive, GLenum mode) {
+  _vertices_immediate_pcollector.add_level(primitive->get_num_vertices());
+  CPTA_int ends = primitive->get_ends();
+      
+  if (primitive->is_indexed()) {
+    int begin = 0;
+    CPTA_int::const_iterator ei;
+    for (ei = ends.begin(); ei != ends.end(); ++ei) {
+      int end = (*ei);
+
+      GLP(Begin)(mode);
+      for (int v = begin; v < end; ++v) {
+        _sender.set_vertex(primitive->get_vertex(v));
+        _sender.issue_vertex();
+      }
+      GLP(End)();
+      
+      begin = end;
+    }
+
+  } else {
+    _sender.set_vertex(primitive->get_first_vertex());
+    int begin = 0;
+    CPTA_int::const_iterator ei;
+    for (ei = ends.begin(); ei != ends.end(); ++ei) {
+      int end = (*ei);
+
+      GLP(Begin)(mode);
+      for (int v = begin; v < end; ++v) {
+        _sender.issue_vertex();
+      }
+      GLP(End)();
+      
+      begin = end;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::report_errors_loop
 //       Access: Protected, Static
 //       Access: Protected, Static
 //  Description: The internal implementation of report_errors().
 //  Description: The internal implementation of report_errors().
 //               Don't call this function; use report_errors()
 //               Don't call this function; use report_errors()

+ 14 - 2
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -54,7 +54,10 @@ typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start,
 typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
 typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
 typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
 typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
 typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
 typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
-typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, const GLfloat s);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, const GLfloat s, const GLfloat t);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, const GLfloat s, const GLfloat t, const GLfloat r);
+typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, const GLfloat s, const GLfloat t, const GLfloat r, const GLfloat q);
 typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);
 typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);
 typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
 typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
 
 
@@ -154,6 +157,8 @@ public:
   void dump_state(void);
   void dump_state(void);
 
 
   const float *get_light_color(Light *light) const;
   const float *get_light_color(Light *light) const;
+  void draw_immediate_simple_primitives(const GeomPrimitive *primitive, GLenum mode);
+  void draw_immediate_composite_primitives(const GeomPrimitive *primitive, GLenum mode);
 
 
   INLINE static bool report_errors(int line, const char *source_file);
   INLINE static bool report_errors(int line, const char *source_file);
   INLINE void report_my_errors(int line, const char *source_file);
   INLINE void report_my_errors(int line, const char *source_file);
@@ -301,6 +306,9 @@ protected:
   bool _vertex_blending_enabled;
   bool _vertex_blending_enabled;
 
 
   CPT(DisplayRegion) _actual_display_region;
   CPT(DisplayRegion) _actual_display_region;
+  CLP(ImmediateModeSender) _sender;
+  bool _use_sender;
+
 #ifdef HAVE_CGGL
 #ifdef HAVE_CGGL
   PT(CgShader) _cg_shader; // The current CgShader object
   PT(CgShader) _cg_shader; // The current CgShader object
   typedef pmap< PT(CgShader), PT(CLP(CgShaderContext)) > CGSHADERCONTEXTS;
   typedef pmap< PT(CgShader), PT(CLP(CgShaderContext)) > CGSHADERCONTEXTS;
@@ -346,7 +354,10 @@ public:
   bool _supports_multitexture;
   bool _supports_multitexture;
   PFNGLACTIVETEXTUREPROC _glActiveTexture;
   PFNGLACTIVETEXTUREPROC _glActiveTexture;
   PFNGLCLIENTACTIVETEXTUREPROC _glClientActiveTexture;
   PFNGLCLIENTACTIVETEXTUREPROC _glClientActiveTexture;
-  PFNGLMULTITEXCOORD2FVPROC _glMultiTexCoord2fv;
+  PFNGLMULTITEXCOORD1FPROC _glMultiTexCoord1f;
+  PFNGLMULTITEXCOORD2FPROC _glMultiTexCoord2f;
+  PFNGLMULTITEXCOORD3FPROC _glMultiTexCoord3f;
+  PFNGLMULTITEXCOORD4FPROC _glMultiTexCoord4f;
 
 
   bool _supports_buffers;
   bool _supports_buffers;
   PFNGLGENBUFFERSPROC _glGenBuffers;
   PFNGLGENBUFFERSPROC _glGenBuffers;
@@ -372,6 +383,7 @@ public:
   static PStatCollector _load_display_list_pcollector;
   static PStatCollector _load_display_list_pcollector;
   static PStatCollector _primitive_batches_display_list_pcollector;
   static PStatCollector _primitive_batches_display_list_pcollector;
   static PStatCollector _vertices_display_list_pcollector;
   static PStatCollector _vertices_display_list_pcollector;
+  static PStatCollector _vertices_immediate_pcollector;
 
 
 public:
 public:
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {

+ 157 - 0
panda/src/glstuff/glImmediateModeSender_src.I

@@ -0,0 +1,157 @@
+// Filename: glImmediateModeSender_src.I
+// Created by:  drose (15Aug05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::
+CLP(ImmediateModeSender)() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender::Constructor
+//       Access: Public
+//  Description: The ComponentSender becomes the owner of the
+//               GeomVertexReader pointer, and will delete it when it
+//               is done.
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::ComponentSender::
+ComponentSender(GeomVertexReader *reader) :
+  _reader(reader)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender::set_vertex
+//       Access: Public
+//  Description: Specifies the vertex index of the next vertex to
+//               send.  If this is not called, the next consecutive
+//               vertex will be sent.
+////////////////////////////////////////////////////////////////////
+INLINE void CLP(ImmediateModeSender)::ComponentSender::
+set_vertex(int vertex_index) {
+  _reader->set_row(vertex_index);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender1f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::ComponentSender1f::
+ComponentSender1f(GeomVertexReader *reader, Func1f *func) :
+  ComponentSender(reader),
+  _func(func)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender2f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::ComponentSender2f::
+ComponentSender2f(GeomVertexReader *reader, Func2f *func) :
+  ComponentSender(reader),
+  _func(func)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender3f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::ComponentSender3f::
+ComponentSender3f(GeomVertexReader *reader, Func3f *func) :
+  ComponentSender(reader),
+  _func(func)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender4f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::ComponentSender4f::
+ComponentSender4f(GeomVertexReader *reader, Func4f *func) :
+  ComponentSender(reader),
+  _func(func)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::TexcoordSender1f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::TexcoordSender1f::
+TexcoordSender1f(GeomVertexReader *reader, TexcoordFunc1f *func, 
+                 int stage_index) :
+  ComponentSender(reader),
+  _func(func),
+  _stage_index(stage_index)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::TexcoordSender2f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::TexcoordSender2f::
+TexcoordSender2f(GeomVertexReader *reader, TexcoordFunc2f *func, 
+                 int stage_index) :
+  ComponentSender(reader),
+  _func(func),
+  _stage_index(stage_index)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::TexcoordSender3f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::TexcoordSender3f::
+TexcoordSender3f(GeomVertexReader *reader, TexcoordFunc3f *func, 
+                 int stage_index) :
+  ComponentSender(reader),
+  _func(func),
+  _stage_index(stage_index)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::TexcoordSender4f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLP(ImmediateModeSender)::TexcoordSender4f::
+TexcoordSender4f(GeomVertexReader *reader, TexcoordFunc4f *func, 
+                 int stage_index) :
+  ComponentSender(reader),
+  _func(func),
+  _stage_index(stage_index)
+{
+}

+ 362 - 0
panda/src/glstuff/glImmediateModeSender_src.cxx

@@ -0,0 +1,362 @@
+// Filename: glImmediateModeSender_src.cxx
+// Created by:  drose (15Aug05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CLP(ImmediateModeSender)::
+~CLP(ImmediateModeSender)() {
+  clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::clear
+//       Access: Public
+//  Description: Removes (and deletes) all of the senders from the
+//               object.
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::
+clear() {
+  ComponentSenders::iterator si;
+  for (si = _senders.begin(); si != _senders.end(); ++si) {
+    delete (*si);
+  }
+  _senders.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::set_vertex
+//       Access: Public
+//  Description: Specifies the vertex index of the next vertex to
+//               send.  If this is not called, the next consecutive
+//               vertex will be sent.
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::
+set_vertex(int vertex_index) {
+  ComponentSenders::iterator si;
+  for (si = _senders.begin(); si != _senders.end(); ++si) {
+    (*si)->set_vertex(vertex_index);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::issue_vertex
+//       Access: Public
+//  Description: Sends the next vertex to the OpenGL API.
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::
+issue_vertex() {
+  ComponentSenders::iterator si;
+  for (si = _senders.begin(); si != _senders.end(); ++si) {
+    (*si)->issue_vertex();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::add_column
+//       Access: Public
+//  Description: Creates a new ComponentSender for the named data
+//               column, if it exists in the vertex data, and adds it
+//               to the list of senders for this object.
+//
+//               The four function pointers are the four variants on
+//               the function pointer for the possible number of
+//               components of the data column.  The appropriate
+//               pointer will be used, depending on the number of
+//               components the data column actually uses.
+//
+//               The return value is true if the column is added,
+//               false if it is not for some reason (for instance, the
+//               named column doesn't exist in the vertex data).
+////////////////////////////////////////////////////////////////////
+bool CLP(ImmediateModeSender)::
+add_column(const GeomVertexData *vertex_data, const InternalName *name,
+           Func1f *func1f, Func2f *func2f, Func3f *func3f, Func4f *func4f) {
+  if (vertex_data->has_column(name)) {
+    GeomVertexReader *reader = new GeomVertexReader(vertex_data, name);
+    ComponentSender *sender = NULL;
+    const GeomVertexColumn *column = reader->get_column();
+    switch (column->get_num_components()) {
+    case 1:
+      if (func1f != (Func1f *)NULL) {
+        sender = new ComponentSender1f(reader, func1f);
+      }
+      break;
+
+    case 2:
+      if (func2f != (Func2f *)NULL) {
+        sender = new ComponentSender2f(reader, func2f);
+      }
+      break;
+
+    case 3:
+      if (func3f != (Func3f *)NULL) {
+        sender = new ComponentSender3f(reader, func3f);
+      }
+      break;
+
+    case 4:
+      if (func4f != (Func4f *)NULL) {
+        sender = new ComponentSender4f(reader, func4f);
+      }
+      break;
+    }
+
+    if (sender != (ComponentSender *)NULL) {
+      // Ok, we've got a valid sender; add it to the list.
+      _senders.push_back(sender);
+      return true;
+
+    } else {
+      // We didn't get a valid sender; clean up and return.
+      delete reader;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::add_texcoord_column
+//       Access: Public
+//  Description: Creates a new ComponentSender for the named data
+//               column, if it exists in the vertex data, and adds it
+//               to the list of senders for this object.
+//
+//               This works like add_column(), but it specifically
+//               handles a texcoord-style column, which requires one
+//               additional parameter to OpenGL: the texture stage.
+//
+//               The return value is true if the column is added,
+//               false if it is not for some reason (for instance, the
+//               named column doesn't exist in the vertex data).
+////////////////////////////////////////////////////////////////////
+bool CLP(ImmediateModeSender)::
+add_texcoord_column(const GeomVertexData *vertex_data, 
+                    const InternalName *name, int stage_index,
+                    TexcoordFunc1f *func1f, TexcoordFunc2f *func2f, 
+                    TexcoordFunc3f *func3f, TexcoordFunc4f *func4f) {
+  if (vertex_data->has_column(name)) {
+    GeomVertexReader *reader = new GeomVertexReader(vertex_data, name);
+    ComponentSender *sender = NULL;
+    const GeomVertexColumn *column = reader->get_column();
+    switch (column->get_num_components()) {
+    case 1:
+      sender = new TexcoordSender1f(reader, func1f, stage_index);
+      break;
+
+    case 2:
+      sender = new TexcoordSender2f(reader, func2f, stage_index);
+      break;
+
+    case 3:
+      sender = new TexcoordSender3f(reader, func3f, stage_index);
+      break;
+
+    case 4:
+      sender = new TexcoordSender4f(reader, func4f, stage_index);
+      break;
+    }
+
+    if (sender != (ComponentSender *)NULL) {
+      // Ok, we've got a valid sender; add it to the list.
+      _senders.push_back(sender);
+      return true;
+
+    } else {
+      // We didn't get a valid sender; clean up and return.
+      delete reader;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::add_sender
+//       Access: Public
+//  Description: Adds a new ComponentSender to the list of senders for
+//               this object.  The GLImmediateModeSender object
+//               becomes the owner of the ComponentSender pointer and
+//               will delete it when it is done.
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::
+add_sender(ComponentSender *sender) {
+  _senders.push_back(sender);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CLP(ImmediateModeSender)::ComponentSender::
+~ComponentSender() {
+  delete _reader;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender1f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::ComponentSender1f::
+issue_vertex() {
+  float d = _reader->get_data1f();
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam()
+      << *_reader->get_column()->get_name() << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(d);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender2f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::ComponentSender2f::
+issue_vertex() {
+  const LVecBase2f &d = _reader->get_data2f();
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam()
+      << *_reader->get_column()->get_name() << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(d[0], d[1]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender3f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::ComponentSender3f::
+issue_vertex() {
+  const LVecBase3f &d = _reader->get_data3f();
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam()
+      << *_reader->get_column()->get_name() << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(d[0], d[1], d[2]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::ComponentSender4f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::ComponentSender4f::
+issue_vertex() {
+  const LVecBase4f &d = _reader->get_data4f();
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam()
+      << *_reader->get_column()->get_name() << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(d[0], d[1], d[2], d[3]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::TexcoordSender1f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::TexcoordSender1f::
+issue_vertex() {
+  float d = _reader->get_data1f();
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam()
+      << *_reader->get_column()->get_name() << ", stage " << _stage_index
+      << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(GL_TEXTURE0 + _stage_index, d);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::TexcoordSender2f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::TexcoordSender2f::
+issue_vertex() {
+  const LVecBase2f &d = _reader->get_data2f();
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam()
+      << *_reader->get_column()->get_name() << ", stage " << _stage_index
+      << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(GL_TEXTURE0 + _stage_index, d[0], d[1]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::TexcoordSender3f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::TexcoordSender3f::
+issue_vertex() {
+  const LVecBase3f &d = _reader->get_data3f();
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam()
+      << *_reader->get_column()->get_name() << ", stage " << _stage_index
+      << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(GL_TEXTURE0 + _stage_index, d[0], d[1], d[2]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(ImmediateModeSender)::TexcoordSender4f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLP(ImmediateModeSender)::TexcoordSender4f::
+issue_vertex() {
+  const LVecBase4f &d = _reader->get_data4f();
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam()
+      << *_reader->get_column()->get_name() << ", stage " << _stage_index
+      << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(GL_TEXTURE0 + _stage_index, d[0], d[1], d[2], d[3]);
+}

+ 156 - 0
panda/src/glstuff/glImmediateModeSender_src.h

@@ -0,0 +1,156 @@
+// Filename: glImmediateModeSender_src.h
+// Created by:  drose (15Aug05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pandabase.h"
+#include "geomVertexReader.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : GLImmediateModeSender
+// Description : This class collects together a handful of objects
+//               that will issue immediate-mode commands like
+//               glVertex, glTexCoord, etc., for the purposes of
+//               sending an object's vertices using the immediate mode
+//               functions.
+//
+//               Normally, this isn't used, since it's almost always
+//               better to use vertex arrays or vertex buffers
+//               instead.  But OpenGL is a complicated API, and some
+//               drivers might have issues handling the vertex arrays;
+//               this code is provided as a last-ditch fallback in
+//               case you have such spectacularly buggy drivers.
+////////////////////////////////////////////////////////////////////
+class EXPCL_GL CLP(ImmediateModeSender) {
+public:
+  INLINE CLP(ImmediateModeSender)();
+  ~CLP(ImmediateModeSender)();
+
+  void clear();
+  
+  void set_vertex(int vertex_index);
+  void issue_vertex();
+
+  class ComponentSender;
+  typedef void GLAPIENTRY (Func1f)(GLfloat a);
+  typedef void GLAPIENTRY (Func2f)(GLfloat a, GLfloat b);
+  typedef void GLAPIENTRY (Func3f)(GLfloat a, GLfloat b, GLfloat c);
+  typedef void GLAPIENTRY (Func4f)(GLfloat a, GLfloat b, GLfloat c, GLfloat d);
+  typedef void GLAPIENTRY (TexcoordFunc1f)(GLenum texture, GLfloat a);
+  typedef void GLAPIENTRY (TexcoordFunc2f)(GLenum texture, GLfloat a, GLfloat b);
+  typedef void GLAPIENTRY (TexcoordFunc3f)(GLenum texture, GLfloat a, GLfloat b, GLfloat c);
+  typedef void GLAPIENTRY (TexcoordFunc4f)(GLenum texture, GLfloat a, GLfloat b, GLfloat c, GLfloat d);
+
+  bool add_column(const GeomVertexData *vertex_data, const InternalName *name,
+                  Func1f *func1f, Func2f *func2f, Func3f *func3f, Func4f *func4f);
+  bool add_texcoord_column(const GeomVertexData *vertex_data, 
+                           const InternalName *name, int stage_index,
+                           TexcoordFunc1f *func1f, TexcoordFunc2f *func2f, 
+                           TexcoordFunc3f *func3f, TexcoordFunc4f *func4f);
+
+  void add_sender(ComponentSender *sender);
+
+public:
+
+  class ComponentSender {
+  public:
+    INLINE ComponentSender(GeomVertexReader *reader);
+    virtual ~ComponentSender();
+    INLINE void set_vertex(int vertex_index);
+    virtual void issue_vertex()=0;
+  protected:
+    GeomVertexReader *_reader;
+  };
+
+  class ComponentSender1f : public ComponentSender {
+  public:
+    INLINE ComponentSender1f(GeomVertexReader *reader, Func1f *func);
+    virtual void issue_vertex();
+  private:
+    Func1f *_func;
+  };
+
+  class ComponentSender2f : public ComponentSender {
+  public:
+    INLINE ComponentSender2f(GeomVertexReader *reader, Func2f *func);
+    virtual void issue_vertex();
+  private:
+    Func2f *_func;
+  };
+
+  class ComponentSender3f : public ComponentSender {
+  public:
+    INLINE ComponentSender3f(GeomVertexReader *reader, Func3f *func);
+    virtual void issue_vertex();
+  private:
+    Func3f *_func;
+  };
+
+  class ComponentSender4f : public ComponentSender {
+  public:
+    INLINE ComponentSender4f(GeomVertexReader *reader, Func4f *func);
+    virtual void issue_vertex();
+  private:
+    Func4f *_func;
+  };
+
+  class TexcoordSender1f : public ComponentSender {
+  public:
+    INLINE TexcoordSender1f(GeomVertexReader *reader, TexcoordFunc1f *func,
+                            int stage_index);
+    virtual void issue_vertex();
+  private:
+    TexcoordFunc1f *_func;
+    int _stage_index;
+  };
+
+  class TexcoordSender2f : public ComponentSender {
+  public:
+    INLINE TexcoordSender2f(GeomVertexReader *reader, TexcoordFunc2f *func,
+                            int stage_index);
+    virtual void issue_vertex();
+  private:
+    TexcoordFunc2f *_func;
+    int _stage_index;
+  };
+
+  class TexcoordSender3f : public ComponentSender {
+  public:
+    INLINE TexcoordSender3f(GeomVertexReader *reader, TexcoordFunc3f *func,
+                            int stage_index);
+    virtual void issue_vertex();
+  private:
+    TexcoordFunc3f *_func;
+    int _stage_index;
+  };
+
+  class TexcoordSender4f : public ComponentSender {
+  public:
+    INLINE TexcoordSender4f(GeomVertexReader *reader, TexcoordFunc4f *func,
+                            int stage_index);
+    virtual void issue_vertex();
+  private:
+    TexcoordFunc4f *_func;
+    int _stage_index;
+  };
+
+private:
+  typedef pvector<ComponentSender *> ComponentSenders;
+  ComponentSenders _senders;
+};
+
+#include "glImmediateModeSender_src.I"
+

+ 1 - 0
panda/src/glstuff/glstuff_src.cxx

@@ -28,6 +28,7 @@
 #include "glGeomContext_src.cxx"
 #include "glGeomContext_src.cxx"
 #include "glGeomMunger_src.cxx"
 #include "glGeomMunger_src.cxx"
 #include "glCgShaderContext_src.cxx"
 #include "glCgShaderContext_src.cxx"
+#include "glImmediateModeSender_src.cxx"
 #include "glGraphicsStateGuardian_src.cxx"
 #include "glGraphicsStateGuardian_src.cxx"
 
 
 
 

+ 2 - 2
panda/src/glstuff/glstuff_src.h

@@ -37,7 +37,7 @@
 // This file is not protected from multiple inclusion; it may need to
 // This file is not protected from multiple inclusion; it may need to
 // be included multiple times.
 // be included multiple times.
 
 
-#include "glext.h"
+#include "panda_glext.h"
 
 
 #include "glmisc_src.h"
 #include "glmisc_src.h"
 #include "glTextureContext_src.h"
 #include "glTextureContext_src.h"
@@ -46,6 +46,6 @@
 #include "glGeomContext_src.h"
 #include "glGeomContext_src.h"
 #include "glGeomMunger_src.h"
 #include "glGeomMunger_src.h"
 #include "glCgShaderContext_src.h"
 #include "glCgShaderContext_src.h"
+#include "glImmediateModeSender_src.h"
 #include "glGraphicsStateGuardian_src.h"
 #include "glGraphicsStateGuardian_src.h"
 
 
-

+ 0 - 0
panda/src/glstuff/glext.h → panda/src/glstuff/panda_glext.h


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

@@ -87,6 +87,21 @@ ConfigVariableBool vertex_buffers
           "or offscreen buffers).  On lower-end graphics cards this will "
           "or offscreen buffers).  On lower-end graphics cards this will "
           "make little or no difference."));
           "make little or no difference."));
 
 
+ConfigVariableBool vertex_arrays
+("vertex-arrays", true,
+ PRC_DESC("Set this true to allow the use of vertex arrays for rendering "
+          "OpenGL vertex data.  This, or vertex buffers, is the normal "
+          "way of issuing vertices ever since OpenGL 1.1, and you "
+          "almost always want to have set to true.  However, some very "
+          "buggy graphics drivers may have problems handling vertex arrays "
+          "correctly, so if you are experiencing problems you might try "
+          "setting this to false.  If this is false, Panda will fall back "
+          "to using immediate-mode commands like glVertex3f(), etc., to "
+          "issue the vertices, which is potentially much slower than "
+          "vertex arrays.  Setting this false causes vertex_buffers to be "
+          "ignored (since vertex buffers are a special case of vertex "
+          "arrays in OpenGL).  This has no effect on DirectX rendering."));
+
 ConfigVariableBool display_lists
 ConfigVariableBool display_lists
 ("display-lists", false,
 ("display-lists", false,
  PRC_DESC("Set this true to allow the use of OpenGL display lists for "
  PRC_DESC("Set this true to allow the use of OpenGL display lists for "

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

@@ -50,6 +50,7 @@ EXPCL_PANDA istream &operator >> (istream &in, AutoTextureScale &ats);
 extern EXPCL_PANDA ConfigVariableInt max_texture_dimension;
 extern EXPCL_PANDA ConfigVariableInt max_texture_dimension;
 extern EXPCL_PANDA ConfigVariableBool keep_texture_ram;
 extern EXPCL_PANDA ConfigVariableBool keep_texture_ram;
 extern EXPCL_PANDA ConfigVariableBool vertex_buffers;
 extern EXPCL_PANDA ConfigVariableBool vertex_buffers;
+extern EXPCL_PANDA ConfigVariableBool vertex_arrays;
 extern EXPCL_PANDA ConfigVariableBool display_lists;
 extern EXPCL_PANDA ConfigVariableBool display_lists;
 extern EXPCL_PANDA ConfigVariableBool hardware_animated_vertices;
 extern EXPCL_PANDA ConfigVariableBool hardware_animated_vertices;
 extern EXPCL_PANDA ConfigVariableBool hardware_point_sprites;
 extern EXPCL_PANDA ConfigVariableBool hardware_point_sprites;