瀏覽代碼

Separate out sampler state from texture, support sampler objects, support LOD min/max/bias

rdb 11 年之前
父節點
當前提交
95d85819b0
共有 76 個文件被更改,包括 3711 次插入1292 次删除
  1. 4 4
      panda/src/display/graphicsOutput.cxx
  2. 27 0
      panda/src/display/graphicsStateGuardian.I
  3. 39 8
      panda/src/display/graphicsStateGuardian.cxx
  4. 7 1
      panda/src/display/graphicsStateGuardian.h
  5. 1 1
      panda/src/doc/howto.use_multipass.txt
  6. 6 6
      panda/src/dxgsg8/dxGraphicsStateGuardian8.I
  7. 21 21
      panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx
  8. 3 3
      panda/src/dxgsg8/dxGraphicsStateGuardian8.h
  9. 28 28
      panda/src/dxgsg8/dxTextureContext8.cxx
  10. 6 6
      panda/src/dxgsg9/dxGraphicsStateGuardian9.I
  11. 34 42
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  12. 5 4
      panda/src/dxgsg9/dxGraphicsStateGuardian9.h
  13. 49 44
      panda/src/dxgsg9/dxShaderContext9.cxx
  14. 28 28
      panda/src/dxgsg9/dxTextureContext9.cxx
  15. 27 27
      panda/src/egg2pg/eggLoader.cxx
  16. 1 1
      panda/src/egg2pg/eggLoader.h
  17. 12 12
      panda/src/egg2pg/eggSaver.cxx
  18. 4 4
      panda/src/framework/windowFramework.cxx
  19. 9 2
      panda/src/glstuff/glCgShaderContext_src.cxx
  20. 259 64
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  21. 26 5
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  22. 83 0
      panda/src/glstuff/glSamplerContext_src.cxx
  23. 58 0
      panda/src/glstuff/glSamplerContext_src.h
  24. 5 0
      panda/src/glstuff/glShaderContext_src.cxx
  25. 3 0
      panda/src/glstuff/glTextureContext_src.h
  26. 7 0
      panda/src/glstuff/glmisc_src.cxx
  27. 1 0
      panda/src/glstuff/glmisc_src.h
  28. 1 0
      panda/src/glstuff/glstuff_src.cxx
  29. 1 0
      panda/src/glstuff/glstuff_src.h
  30. 20 1
      panda/src/gobj/config_gobj.cxx
  31. 1 0
      panda/src/gobj/config_gobj.h
  32. 2 0
      panda/src/gobj/p3gobj_composite1.cxx
  33. 3 2
      panda/src/gobj/p3gobj_composite2.cxx
  34. 158 0
      panda/src/gobj/paramTexture.I
  35. 220 0
      panda/src/gobj/paramTexture.cxx
  36. 143 0
      panda/src/gobj/paramTexture.h
  37. 8 5
      panda/src/gobj/preparedGraphicsObjects.I
  38. 232 13
      panda/src/gobj/preparedGraphicsObjects.cxx
  39. 31 6
      panda/src/gobj/preparedGraphicsObjects.h
  40. 25 0
      panda/src/gobj/samplerContext.I
  41. 38 0
      panda/src/gobj/samplerContext.cxx
  42. 73 0
      panda/src/gobj/samplerContext.h
  43. 338 0
      panda/src/gobj/samplerState.I
  44. 401 0
      panda/src/gobj/samplerState.cxx
  45. 210 0
      panda/src/gobj/samplerState.h
  46. 163 47
      panda/src/gobj/texture.I
  47. 70 355
      panda/src/gobj/texture.cxx
  48. 83 122
      panda/src/gobj/texture.h
  49. 2 2
      panda/src/gobj/textureContext.cxx
  50. 2 2
      panda/src/grutil/multitexReducer.cxx
  51. 6 1
      panda/src/gsgbase/graphicsStateGuardianBase.h
  52. 2 2
      panda/src/osxdisplay/osxGraphicsStateGuardian.cxx
  53. 40 38
      panda/src/pgraph/geomNode.cxx
  54. 228 118
      panda/src/pgraph/nodePath.cxx
  55. 20 14
      panda/src/pgraph/nodePath.h
  56. 11 11
      panda/src/pgraph/occluderNode.cxx
  57. 98 55
      panda/src/pgraph/shaderAttrib.cxx
  58. 8 9
      panda/src/pgraph/shaderAttrib.h
  59. 124 68
      panda/src/pgraph/shaderInput.I
  60. 21 12
      panda/src/pgraph/shaderInput.h
  61. 26 8
      panda/src/pgraph/textureAttrib.I
  62. 68 14
      panda/src/pgraph/textureAttrib.cxx
  63. 8 4
      panda/src/pgraph/textureAttrib.h
  64. 4 4
      panda/src/pgraphnodes/spotlight.cxx
  65. 2 1
      panda/src/putil/bam.h
  66. 6 6
      panda/src/rocket/rocketRenderInterface.cxx
  67. 1 1
      panda/src/testbed/test_texmem.cxx
  68. 2 2
      panda/src/testbed/text_test.cxx
  69. 6 6
      panda/src/text/config_text.cxx
  70. 3 3
      panda/src/text/config_text.h
  71. 4 4
      panda/src/text/dynamicTextFont.I
  72. 6 6
      panda/src/text/dynamicTextFont.h
  73. 2 2
      panda/src/text/staticTextFont.cxx
  74. 34 34
      panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx
  75. 2 2
      panda/src/tinydisplay/tinyGraphicsStateGuardian.h
  76. 1 1
      pandatool/src/pfmprogs/pfmTrans.cxx

+ 4 - 4
panda/src/display/graphicsOutput.cxx

@@ -308,8 +308,8 @@ add_render_texture(Texture *tex, RenderTextureMode mode,
   // Create texture if necessary.
   if (tex == (Texture *)NULL) {
     tex = new Texture(get_name());
-    tex->set_wrap_u(Texture::WM_clamp);
-    tex->set_wrap_v(Texture::WM_clamp);
+    tex->set_wrap_u(SamplerState::WM_clamp);
+    tex->set_wrap_v(SamplerState::WM_clamp);
   } else {
     tex->clear_ram_image();
   }
@@ -1050,8 +1050,8 @@ make_cube_map(const string &name, int size, NodePath &camera_rig,
 
   PT(Texture) tex = new Texture(name);
   tex->setup_cube_map();
-  tex->set_wrap_u(Texture::WM_clamp);
-  tex->set_wrap_v(Texture::WM_clamp);
+  tex->set_wrap_u(SamplerState::WM_clamp);
+  tex->set_wrap_v(SamplerState::WM_clamp);
   GraphicsOutput *buffer;
 
   buffer = make_texture_buffer(name, size, size, tex, to_ram, fbp);

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

@@ -36,6 +36,17 @@ release_all_textures() {
   return _prepared_objects->release_all_textures();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::release_all_samplers
+//       Access: Public
+//  Description: Frees the resources for all samplers associated with
+//               this GSG.
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsStateGuardian::
+release_all_samplers() {
+  return _prepared_objects->release_all_samplers();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::release_all_geoms
 //       Access: Public
@@ -632,6 +643,22 @@ get_supports_shadow_filter() const {
   return _supports_shadow_filter;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_sampler_objects
+//       Access: Published
+//  Description: Returns true if this particular GSG supports the
+//               use of sampler objects to record texture sampling
+//               parameters separately from the texture objects.
+//               This doesn't really affect functionality, but if
+//               this is false, it may mean that using the same
+//               texture with different SamplerState objects will
+//               result in reduced performance.
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsStateGuardian::
+get_supports_sampler_objects() const {
+  return _supports_sampler_objects;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_supports_basic_shaders
 //       Access: Published

+ 39 - 8
panda/src/display/graphicsStateGuardian.cxx

@@ -98,6 +98,7 @@ PStatCollector GraphicsStateGuardian::_command_latency_pcollector("Command laten
 
 PStatCollector GraphicsStateGuardian::_prepare_pcollector("Draw:Prepare");
 PStatCollector GraphicsStateGuardian::_prepare_texture_pcollector("Draw:Prepare:Texture");
+PStatCollector GraphicsStateGuardian::_prepare_sampler_pcollector("Draw:Prepare:Sampler");
 PStatCollector GraphicsStateGuardian::_prepare_geom_pcollector("Draw:Prepare:Geom");
 PStatCollector GraphicsStateGuardian::_prepare_shader_pcollector("Draw:Prepare:Shader");
 PStatCollector GraphicsStateGuardian::_prepare_vertex_buffer_pcollector("Draw:Prepare:Vertex buffer");
@@ -230,6 +231,7 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _supports_depth_texture = false;
   _supports_depth_stencil = false;
   _supports_shadow_filter = false;
+  _supports_sampler_objects = false;
   _supports_basic_shaders = false;
   _supports_geometry_shaders = false;
   _supports_tessellation_shaders = false;
@@ -626,6 +628,35 @@ extract_texture_data(Texture *) {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::prepare_sampler
+//       Access: Public, Virtual
+//  Description: Creates whatever structures the GSG requires to
+//               represent the sampler internally, and returns a
+//               newly-allocated SamplerContext object with this data.
+//               It is the responsibility of the calling function to
+//               later call release_sampler() with this same pointer
+//               (which will also delete the pointer).
+//
+//               This function should not be called directly to
+//               prepare a sampler.  Instead, call Texture::prepare().
+////////////////////////////////////////////////////////////////////
+SamplerContext *GraphicsStateGuardian::
+prepare_sampler(const SamplerState &sampler) {
+  return (SamplerContext *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::release_sampler
+//       Access: Public, Virtual
+//  Description: Frees the resources previously allocated via a call
+//               to prepare_sampler(), including deleting the
+//               SamplerContext itself, if it is non-NULL.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+release_sampler(SamplerContext *) {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::prepare_geom
 //       Access: Public, Virtual
@@ -2883,22 +2914,22 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
 
   // Set the wrap mode
   if (is_point) {
-    tex->set_wrap_u(Texture::WM_clamp);
-    tex->set_wrap_v(Texture::WM_clamp);
+    tex->set_wrap_u(SamplerState::WM_clamp);
+    tex->set_wrap_v(SamplerState::WM_clamp);
   } else {
-    tex->set_wrap_u(Texture::WM_border_color);
-    tex->set_wrap_v(Texture::WM_border_color);
+    tex->set_wrap_u(SamplerState::WM_border_color);
+    tex->set_wrap_v(SamplerState::WM_border_color);
     tex->set_border_color(LVecBase4(1, 1, 1, 1));
   }
 
   if (get_supports_shadow_filter()) {
     // If we have the ARB_shadow extension, enable shadow filtering.
-    tex->set_minfilter(Texture::FT_shadow);
-    tex->set_magfilter(Texture::FT_shadow);
+    tex->set_minfilter(SamplerState::FT_shadow);
+    tex->set_magfilter(SamplerState::FT_shadow);
   } else {
     // We only accept linear - this tells the GPU to use hardware PCF.
-    tex->set_minfilter(Texture::FT_linear);
-    tex->set_magfilter(Texture::FT_linear);
+    tex->set_minfilter(SamplerState::FT_linear);
+    tex->set_magfilter(SamplerState::FT_linear);
   }
 
   // Assign display region(s) to the buffer and camera

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

@@ -87,6 +87,7 @@ PUBLISHED:
 
   INLINE void release_all();
   INLINE int release_all_textures();
+  INLINE int release_all_samplers();
   INLINE int release_all_geoms();
   INLINE int release_all_vertex_buffers();
   INLINE int release_all_index_buffers();
@@ -143,6 +144,7 @@ PUBLISHED:
   INLINE bool get_supports_depth_texture() const;
   INLINE bool get_supports_depth_stencil() const;
   INLINE bool get_supports_shadow_filter() const;
+  INLINE bool get_supports_sampler_objects() const;
   INLINE bool get_supports_basic_shaders() const;
   INLINE bool get_supports_geometry_shaders() const;
   INLINE bool get_supports_tessellation_shaders() const;
@@ -165,7 +167,6 @@ PUBLISHED:
   virtual int get_supported_geom_rendering() const;
   virtual bool get_supports_cg_profile(const string &name) const;
 
-
   INLINE bool get_color_scale_via_lighting() const;
   INLINE bool get_alpha_scale_via_texture() const;
   INLINE bool get_alpha_scale_via_texture(const TextureAttrib *tex_attrib) const;
@@ -215,6 +216,9 @@ public:
   virtual void release_texture(TextureContext *tc);
   virtual bool extract_texture_data(Texture *tex);
 
+  virtual SamplerContext *prepare_sampler(const SamplerState &sampler);
+  virtual void release_sampler(SamplerContext *sc);
+
   virtual GeomContext *prepare_geom(Geom *geom);
   virtual void release_geom(GeomContext *gc);
 
@@ -509,6 +513,7 @@ protected:
   bool _supports_depth_texture;
   bool _supports_depth_stencil;
   bool _supports_shadow_filter;
+  bool _supports_sampler_objects;
   bool _supports_basic_shaders;
   bool _supports_geometry_shaders;
   bool _supports_tessellation_shaders;
@@ -586,6 +591,7 @@ public:
 
   static PStatCollector _prepare_pcollector;
   static PStatCollector _prepare_texture_pcollector;
+  static PStatCollector _prepare_sampler_pcollector;
   static PStatCollector _prepare_geom_pcollector;
   static PStatCollector _prepare_shader_pcollector;
   static PStatCollector _prepare_vertex_buffer_pcollector;

+ 1 - 1
panda/src/doc/howto.use_multipass.txt

@@ -81,7 +81,7 @@ specified a power-of-2 size.)  You can extract this Texture and set
 various properties on it, then apply it to geometry:
 
   Texture *tex = buffer->get_texture();
-  tex->set_minfilter(Texture::FT_linear_mipmap_linear);
+  tex->set_minfilter(SamplerState::FT_linear_mipmap_linear);
   screen.set_texture(tex);
 
 

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

@@ -79,17 +79,17 @@ LColor_to_D3DCOLOR(const LColor &cLColor) {
 //               GL's.
 ////////////////////////////////////////////////////////////////////
 INLINE D3DTEXTUREADDRESS DXGraphicsStateGuardian8::
-get_texture_wrap_mode(Texture::WrapMode wm) {
+get_texture_wrap_mode(SamplerState::WrapMode wm) {
   switch (wm) {
-  case Texture::WM_clamp:
+  case SamplerState::WM_clamp:
     return D3DTADDRESS_CLAMP;
-  case Texture::WM_repeat:
+  case SamplerState::WM_repeat:
     return D3DTADDRESS_WRAP;
-  case Texture::WM_mirror:
+  case SamplerState::WM_mirror:
     return D3DTADDRESS_MIRROR;
-  case Texture::WM_mirror_once:
+  case SamplerState::WM_mirror_once:
     return D3DTADDRESS_MIRRORONCE;
-  case Texture::WM_border_color:
+  case SamplerState::WM_border_color:
     return D3DTADDRESS_BORDER;
   }
   dxgsg8_cat.error() << "Invalid Texture::Mode value" << endl;

+ 21 - 21
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -193,7 +193,7 @@ apply_texture(int i, TextureContext *tc) {
   DXTextureContext8 *dtc = DCAST(DXTextureContext8, tc);
   Texture *tex = tc->get_texture();
 
-  Texture::WrapMode wrap_u, wrap_v, wrap_w;
+  SamplerState::WrapMode wrap_u, wrap_v, wrap_w;
   wrap_u = tex->get_wrap_u();
   wrap_v = tex->get_wrap_v();
   wrap_w = tex->get_wrap_w();
@@ -206,7 +206,7 @@ apply_texture(int i, TextureContext *tc) {
                                     LColor_to_D3DCOLOR(tex->get_border_color()));
 
   uint aniso_degree = tex->get_effective_anisotropic_degree();
-  Texture::FilterType ft = tex->get_effective_magfilter();
+  SamplerState::FilterType ft = tex->get_effective_magfilter();
 
   if (aniso_degree >= 1) {
     _d3d_device->SetTextureStageState(i, D3DTSS_MAXANISOTROPY, aniso_degree);
@@ -214,7 +214,7 @@ apply_texture(int i, TextureContext *tc) {
 
   D3DTEXTUREFILTERTYPE new_mag_filter;
   if (aniso_degree <= 1) {
-    new_mag_filter = ((ft != Texture::FT_nearest) ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+    new_mag_filter = ((ft != SamplerState::FT_nearest) ? D3DTEXF_LINEAR : D3DTEXF_POINT);
   } else {
     new_mag_filter = D3DTEXF_ANISOTROPIC;
   }
@@ -4031,28 +4031,28 @@ copy_pres_reset(DXScreenData *screen) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 D3DTEXTUREFILTERTYPE DXGraphicsStateGuardian8::
-get_d3d_min_type(Texture::FilterType filter_type) {
+get_d3d_min_type(SamplerState::FilterType filter_type) {
   switch (filter_type) {
-  case Texture::FT_nearest:
+  case SamplerState::FT_nearest:
     return D3DTEXF_POINT;
 
-  case Texture::FT_linear:
+  case SamplerState::FT_linear:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_nearest_mipmap_nearest:
+  case SamplerState::FT_nearest_mipmap_nearest:
     return D3DTEXF_POINT;
 
-  case Texture::FT_linear_mipmap_nearest:
+  case SamplerState::FT_linear_mipmap_nearest:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_nearest_mipmap_linear:
+  case SamplerState::FT_nearest_mipmap_linear:
     return D3DTEXF_POINT;
 
-  case Texture::FT_linear_mipmap_linear:
+  case SamplerState::FT_linear_mipmap_linear:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_shadow:
-  case Texture::FT_default:
+  case SamplerState::FT_shadow:
+  case SamplerState::FT_default:
     return D3DTEXF_LINEAR;
   }
 
@@ -4067,28 +4067,28 @@ get_d3d_min_type(Texture::FilterType filter_type) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 D3DTEXTUREFILTERTYPE DXGraphicsStateGuardian8::
-get_d3d_mip_type(Texture::FilterType filter_type) {
+get_d3d_mip_type(SamplerState::FilterType filter_type) {
   switch (filter_type) {
-  case Texture::FT_nearest:
+  case SamplerState::FT_nearest:
     return D3DTEXF_NONE;
 
-  case Texture::FT_linear:
+  case SamplerState::FT_linear:
     return D3DTEXF_NONE;
 
-  case Texture::FT_nearest_mipmap_nearest:
+  case SamplerState::FT_nearest_mipmap_nearest:
     return D3DTEXF_POINT;
 
-  case Texture::FT_linear_mipmap_nearest:
+  case SamplerState::FT_linear_mipmap_nearest:
     return D3DTEXF_POINT;
 
-  case Texture::FT_nearest_mipmap_linear:
+  case SamplerState::FT_nearest_mipmap_linear:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_linear_mipmap_linear:
+  case SamplerState::FT_linear_mipmap_linear:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_shadow:
-  case Texture::FT_default:
+  case SamplerState::FT_shadow:
+  case SamplerState::FT_default:
     return D3DTEXF_NONE;
   }
 

+ 3 - 3
panda/src/dxgsg8/dxGraphicsStateGuardian8.h

@@ -174,7 +174,7 @@ protected:
   void do_auto_rescale_normal();
 
 protected:
-  INLINE static D3DTEXTUREADDRESS get_texture_wrap_mode(Texture::WrapMode wm);
+  INLINE static D3DTEXTUREADDRESS get_texture_wrap_mode(SamplerState::WrapMode wm);
   INLINE static D3DFOGMODE get_fog_mode_type(Fog::Mode m);
   const D3DCOLORVALUE &get_light_color(Light *light) const;
   INLINE static D3DTRANSFORMSTATETYPE get_tex_mat_sym(int stage_index);
@@ -199,8 +199,8 @@ protected:
   bool release_swap_chain (DXScreenData *new_context);
   void copy_pres_reset(DXScreenData *new_context);
 
-  static D3DTEXTUREFILTERTYPE get_d3d_min_type(Texture::FilterType filter_type);
-  static D3DTEXTUREFILTERTYPE get_d3d_mip_type(Texture::FilterType filter_type);
+  static D3DTEXTUREFILTERTYPE get_d3d_min_type(SamplerState::FilterType filter_type);
+  static D3DTEXTUREFILTERTYPE get_d3d_mip_type(SamplerState::FilterType filter_type);
   static D3DTEXTUREOP get_texture_operation(TextureStage::CombineMode mode, int scale);
   static DWORD get_texture_argument(TextureStage::CombineSource source,
                                     TextureStage::CombineOperand operand);

+ 28 - 28
panda/src/dxgsg8/dxTextureContext8.cxx

@@ -591,21 +591,21 @@ create_texture(DXScreenData &scrn) {
   // validate magfilter setting
   // degrade filtering if no HW support
 
-  Texture::FilterType ft;
+  SamplerState::FilterType ft;
 
   ft = tex->get_magfilter();
-  if ((ft != Texture::FT_linear) && ft != Texture::FT_nearest) {
+  if ((ft != SamplerState::FT_linear) && ft != SamplerState::FT_nearest) {
     // mipmap settings make no sense for magfilter
-    if (ft == Texture::FT_nearest_mipmap_nearest) {
-      ft = Texture::FT_nearest;
+    if (ft == SamplerState::FT_nearest_mipmap_nearest) {
+      ft = SamplerState::FT_nearest;
     } else {
-      ft = Texture::FT_linear;
+      ft = SamplerState::FT_linear;
     }
   }
 
-  if (ft == Texture::FT_linear &&
+  if (ft == SamplerState::FT_linear &&
       (filter_caps & D3DPTFILTERCAPS_MAGFLINEAR) == 0) {
-    ft = Texture::FT_nearest;
+    ft = SamplerState::FT_nearest;
   }
   tex->set_magfilter(ft);
 
@@ -615,33 +615,33 @@ create_texture(DXScreenData &scrn) {
 
   if (!dx_ignore_mipmaps) {  // set if no HW mipmap capable
     switch(ft) {
-    case Texture::FT_nearest_mipmap_nearest:
-    case Texture::FT_linear_mipmap_nearest:
-    case Texture::FT_nearest_mipmap_linear:  // pick nearest in each, interpolate linearly b/w them
-    case Texture::FT_linear_mipmap_linear:
+    case SamplerState::FT_nearest_mipmap_nearest:
+    case SamplerState::FT_linear_mipmap_nearest:
+    case SamplerState::FT_nearest_mipmap_linear:  // pick nearest in each, interpolate linearly b/w them
+    case SamplerState::FT_linear_mipmap_linear:
       _has_mipmaps = true;
     }
 
     if (dx_mipmap_everything) {  // debug toggle, ok to leave in since its just a creation cost
       _has_mipmaps = true;
       if (dxgsg8_cat.is_spam()) {
-        if (ft != Texture::FT_linear_mipmap_linear) {
+        if (ft != SamplerState::FT_linear_mipmap_linear) {
           dxgsg8_cat.spam()
             << "Forcing trilinear mipmapping on DX texture ["
             << tex->get_name() << "]\n";
         }
       }
-      ft = Texture::FT_linear_mipmap_linear;
+      ft = SamplerState::FT_linear_mipmap_linear;
       tex->set_minfilter(ft);
     }
 
-  } else if ((ft == Texture::FT_nearest_mipmap_nearest) ||   // cvt to no-mipmap filter types
-             (ft == Texture::FT_nearest_mipmap_linear)) {
-    ft = Texture::FT_nearest;
+  } else if ((ft == SamplerState::FT_nearest_mipmap_nearest) ||   // cvt to no-mipmap filter types
+             (ft == SamplerState::FT_nearest_mipmap_linear)) {
+    ft = SamplerState::FT_nearest;
 
-  } else if ((ft == Texture::FT_linear_mipmap_nearest) ||
-            (ft == Texture::FT_linear_mipmap_linear)) {
-    ft = Texture::FT_linear;
+  } else if ((ft == SamplerState::FT_linear_mipmap_nearest) ||
+            (ft == SamplerState::FT_linear_mipmap_linear)) {
+    ft = SamplerState::FT_linear;
   }
 
   nassertr((filter_caps & D3DPTFILTERCAPS_MINFPOINT) != 0, false);
@@ -650,36 +650,36 @@ create_texture(DXScreenData &scrn) {
 
   // do any other filter type degradations necessary
   switch(ft) {
-  case Texture::FT_linear_mipmap_linear:
+  case SamplerState::FT_linear_mipmap_linear:
     if ((filter_caps & TRILINEAR_MIPMAP_TEXFILTERCAPS) != TRILINEAR_MIPMAP_TEXFILTERCAPS) {
       if (filter_caps & D3DPTFILTERCAPS_MINFLINEAR) {
-        ft = Texture::FT_linear_mipmap_nearest;
+        ft = SamplerState::FT_linear_mipmap_nearest;
       } else {
         // if you cant do linear in a level, you probably cant do
         // linear b/w levels, so just do nearest-all
-        ft = Texture::FT_nearest_mipmap_nearest;
+        ft = SamplerState::FT_nearest_mipmap_nearest;
       }
     }
     break;
 
-  case Texture::FT_nearest_mipmap_linear:
+  case SamplerState::FT_nearest_mipmap_linear:
     // if we don't have bilinear, do nearest_nearest
     if (!((filter_caps & D3DPTFILTERCAPS_MIPFPOINT) &&
           (filter_caps & D3DPTFILTERCAPS_MINFLINEAR))) {
-      ft = Texture::FT_nearest_mipmap_nearest;
+      ft = SamplerState::FT_nearest_mipmap_nearest;
     }
     break;
 
-  case Texture::FT_linear_mipmap_nearest:
+  case SamplerState::FT_linear_mipmap_nearest:
     // if we don't have mip linear, do nearest_nearest
     if (!(filter_caps & D3DPTFILTERCAPS_MIPFLINEAR)) {
-      ft = Texture::FT_nearest_mipmap_nearest;
+      ft = SamplerState::FT_nearest_mipmap_nearest;
     }
     break;
 
-  case Texture::FT_linear:
+  case SamplerState::FT_linear:
     if (!(filter_caps & D3DPTFILTERCAPS_MINFLINEAR)) {
-      ft = Texture::FT_nearest;
+      ft = SamplerState::FT_nearest;
     }
     break;
   }

+ 6 - 6
panda/src/dxgsg9/dxGraphicsStateGuardian9.I

@@ -79,17 +79,17 @@ LColor_to_D3DCOLOR(const LColor &cLColor) {
 //               GL's.
 ////////////////////////////////////////////////////////////////////
 INLINE D3DTEXTUREADDRESS DXGraphicsStateGuardian9::
-get_texture_wrap_mode(Texture::WrapMode wm) {
+get_texture_wrap_mode(SamplerState::WrapMode wm) {
   switch (wm) {
-  case Texture::WM_clamp:
+  case SamplerState::WM_clamp:
     return D3DTADDRESS_CLAMP;
-  case Texture::WM_repeat:
+  case SamplerState::WM_repeat:
     return D3DTADDRESS_WRAP;
-  case Texture::WM_mirror:
+  case SamplerState::WM_mirror:
     return D3DTADDRESS_MIRROR;
-  case Texture::WM_mirror_once:
+  case SamplerState::WM_mirror_once:
     return D3DTADDRESS_MIRRORONCE;
-  case Texture::WM_border_color:
+  case SamplerState::WM_border_color:
     return D3DTADDRESS_BORDER;
   }
   dxgsg9_cat.error() << "Invalid Texture::Mode value" << endl;

+ 34 - 42
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -206,7 +206,7 @@ prepare_texture(Texture *tex, int view) {
 //               rendering on the ith stage.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
-apply_texture(int i, TextureContext *tc) {
+apply_texture(int i, TextureContext *tc, const SamplerState &sampler) {
   if (tc == (TextureContext *)NULL) {
     // The texture wasn't bound properly or something, so ensure
     // texturing is disabled and just return.
@@ -231,31 +231,22 @@ apply_texture(int i, TextureContext *tc) {
     set_sampler_state(i, D3DSAMP_SRGBTEXTURE, FALSE);
   }
 
-  Texture::WrapMode wrap_u, wrap_v, wrap_w;
+  set_sampler_state(i, D3DSAMP_ADDRESSU,
+    get_texture_wrap_mode(sampler.get_wrap_u()));
 
-  DWORD address_u;
-  DWORD address_v;
-  DWORD address_w;
+  set_sampler_state(i, D3DSAMP_ADDRESSV,
+    get_texture_wrap_mode(sampler.get_wrap_v()));
 
-  wrap_u = tex->get_wrap_u();
-  wrap_v = tex->get_wrap_v();
-  wrap_w = tex->get_wrap_w();
-
-  address_u = get_texture_wrap_mode(wrap_u);
-  address_v = get_texture_wrap_mode(wrap_v);
-  address_w = get_texture_wrap_mode(wrap_w);
-
-  set_sampler_state(i, D3DSAMP_ADDRESSU, address_u);
-  set_sampler_state(i, D3DSAMP_ADDRESSV, address_v);
-  set_sampler_state(i, D3DSAMP_ADDRESSW, address_w);
+  set_sampler_state(i, D3DSAMP_ADDRESSW,
+    get_texture_wrap_mode(sampler.get_wrap_w()));
 
   DWORD border_color;
-  border_color = LColor_to_D3DCOLOR(tex->get_border_color());
+  border_color = LColor_to_D3DCOLOR(sampler.get_border_color());
 
   set_sampler_state(i, D3DSAMP_BORDERCOLOR, border_color);
 
-  uint aniso_degree = tex->get_effective_anisotropic_degree();
-  Texture::FilterType ft = tex->get_effective_magfilter();
+  uint aniso_degree = sampler.get_effective_anisotropic_degree();
+  SamplerState::FilterType ft = sampler.get_effective_magfilter();
 
   if (aniso_degree >= 1) {
     set_sampler_state(i, D3DSAMP_MAXANISOTROPY, aniso_degree);
@@ -266,7 +257,7 @@ apply_texture(int i, TextureContext *tc) {
 
   supports_anisotropic_mag_filter = (_screen -> _d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC) != 0;
   if (aniso_degree <= 1 || supports_anisotropic_mag_filter == 0) {
-    new_mag_filter = ((ft != Texture::FT_nearest) ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+    new_mag_filter = ((ft != SamplerState::FT_nearest) ? D3DTEXF_LINEAR : D3DTEXF_POINT);
   } else {
     new_mag_filter = D3DTEXF_ANISOTROPIC;
   }
@@ -276,12 +267,12 @@ apply_texture(int i, TextureContext *tc) {
   if (hr != D3D_OK) {
     dxgsg9_cat.error()
       << "ERROR: set_sampler_state (D3DSAMP_MAGFILTER, "
-      << new_mag_filter << ") failed for texture:" << tex -> get_name() << endl;
+      << new_mag_filter << ") failed for sampler: " << sampler << endl;
   }
 
   // map Panda composite min+mip filter types to d3d's separate min & mip filter types
-  D3DTEXTUREFILTERTYPE new_min_filter = get_d3d_min_type(tex->get_effective_minfilter());
-  D3DTEXTUREFILTERTYPE new_mip_filter = get_d3d_mip_type(tex->get_effective_minfilter());
+  D3DTEXTUREFILTERTYPE new_min_filter = get_d3d_min_type(sampler.get_effective_minfilter());
+  D3DTEXTUREFILTERTYPE new_mip_filter = get_d3d_mip_type(sampler.get_effective_minfilter());
 
   if (!tex->might_have_ram_image()) {
     // If the texture is completely dynamic, don't try to issue
@@ -3734,12 +3725,13 @@ update_standard_texture_bindings() {
 
     Texture *texture = _target_texture->get_on_texture(stage);
     nassertv(texture != (Texture *)NULL);
+    const SamplerState &sampler = _target_texture->get_on_sampler(stage);
 
     // We always reissue every stage in DX, just in case the texcoord
     // index or texgen mode or some other property has changed.
     int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
     TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
-    apply_texture(si, tc);
+    apply_texture(si, tc, sampler);
     set_texture_blend_mode(si, stage);
 
     int texcoord_dimensions = 2;
@@ -4936,28 +4928,28 @@ copy_pres_reset(DXScreenData *screen) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 D3DTEXTUREFILTERTYPE DXGraphicsStateGuardian9::
-get_d3d_min_type(Texture::FilterType filter_type) {
+get_d3d_min_type(SamplerState::FilterType filter_type) {
   switch (filter_type) {
-  case Texture::FT_nearest:
+  case SamplerState::FT_nearest:
     return D3DTEXF_POINT;
 
-  case Texture::FT_linear:
+  case SamplerState::FT_linear:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_nearest_mipmap_nearest:
+  case SamplerState::FT_nearest_mipmap_nearest:
     return D3DTEXF_POINT;
 
-  case Texture::FT_linear_mipmap_nearest:
+  case SamplerState::FT_linear_mipmap_nearest:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_nearest_mipmap_linear:
+  case SamplerState::FT_nearest_mipmap_linear:
     return D3DTEXF_POINT;
 
-  case Texture::FT_linear_mipmap_linear:
+  case SamplerState::FT_linear_mipmap_linear:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_shadow:
-  case Texture::FT_default:
+  case SamplerState::FT_shadow:
+  case SamplerState::FT_default:
     return D3DTEXF_LINEAR;
   }
 
@@ -4972,28 +4964,28 @@ get_d3d_min_type(Texture::FilterType filter_type) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 D3DTEXTUREFILTERTYPE DXGraphicsStateGuardian9::
-get_d3d_mip_type(Texture::FilterType filter_type) {
+get_d3d_mip_type(SamplerState::FilterType filter_type) {
   switch (filter_type) {
-  case Texture::FT_nearest:
+  case SamplerState::FT_nearest:
     return D3DTEXF_NONE;
 
-  case Texture::FT_linear:
+  case SamplerState::FT_linear:
     return D3DTEXF_NONE;
 
-  case Texture::FT_nearest_mipmap_nearest:
+  case SamplerState::FT_nearest_mipmap_nearest:
     return D3DTEXF_POINT;
 
-  case Texture::FT_linear_mipmap_nearest:
+  case SamplerState::FT_linear_mipmap_nearest:
     return D3DTEXF_POINT;
 
-  case Texture::FT_nearest_mipmap_linear:
+  case SamplerState::FT_nearest_mipmap_linear:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_linear_mipmap_linear:
+  case SamplerState::FT_linear_mipmap_linear:
     return D3DTEXF_LINEAR;
 
-  case Texture::FT_shadow:
-  case Texture::FT_default:
+  case SamplerState::FT_shadow:
+  case SamplerState::FT_default:
     return D3DTEXF_NONE;
   }
 

+ 5 - 4
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -22,6 +22,7 @@
 
 #include "graphicsStateGuardian.h"
 #include "texture.h"
+#include "samplerState.h"
 #include "displayRegion.h"
 #include "material.h"
 #include "depthTestAttrib.h"
@@ -67,7 +68,7 @@ public:
                        DWORD multisampletype, DWORD multisamplequality);
 
   virtual TextureContext *prepare_texture(Texture *tex, int view);
-  void apply_texture(int i, TextureContext *tc);
+  void apply_texture(int i, TextureContext *tc, const SamplerState &sampler);
   virtual bool update_texture(TextureContext *tc, bool force);
   bool upload_texture(DXTextureContext9 *dtc, bool force);
   virtual void release_texture(TextureContext *tc);
@@ -217,7 +218,7 @@ protected:
   void update_standard_texture_bindings();
 
 protected:
-  INLINE static D3DTEXTUREADDRESS get_texture_wrap_mode(Texture::WrapMode wm);
+  INLINE static D3DTEXTUREADDRESS get_texture_wrap_mode(SamplerState::WrapMode wm);
   INLINE static D3DFOGMODE get_fog_mode_type(Fog::Mode m);
   const D3DCOLORVALUE &get_light_color(Light *light) const;
   INLINE static D3DTRANSFORMSTATETYPE get_tex_mat_sym(int stage_index);
@@ -242,8 +243,8 @@ protected:
   bool release_swap_chain (DXScreenData *new_context);
   void copy_pres_reset(DXScreenData *new_context);
 
-  static D3DTEXTUREFILTERTYPE get_d3d_min_type(Texture::FilterType filter_type);
-  static D3DTEXTUREFILTERTYPE get_d3d_mip_type(Texture::FilterType filter_type);
+  static D3DTEXTUREFILTERTYPE get_d3d_min_type(SamplerState::FilterType filter_type);
+  static D3DTEXTUREFILTERTYPE get_d3d_mip_type(SamplerState::FilterType filter_type);
   static D3DTEXTUREOP get_texture_operation(TextureStage::CombineMode mode, int scale);
   DWORD get_texture_argument(TextureStage::CombineSource source,
            TextureStage::CombineOperand operand) const;

+ 49 - 44
panda/src/dxgsg9/dxShaderContext9.cxx

@@ -48,27 +48,27 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) {
 #ifdef HAVE_CG
   _cg_context = 0;
   if (s->get_language() == Shader::SL_Cg) {
-    
-    // Ask the shader to compile itself for us and 
+
+    // Ask the shader to compile itself for us and
     // to give us the resulting Cg program objects.
 
     if (!s->cg_compile_for(gsg->_shader_caps,
                            _cg_context,
                            _cg_vprogram,
-                           _cg_fprogram, 
+                           _cg_fprogram,
                            _cg_gprogram,        // CG2 CHANGE
                            _cg_parameter_map)) {
       return;
     }
-        
+
     // Load the program.
 
     BOOL paramater_shadowing;
     DWORD assembly_flags;
-    
+
     paramater_shadowing = FALSE;
     assembly_flags = 0;
-    
+
 #if DEBUG_SHADER
     assembly_flags |= D3DXSHADER_DEBUG;
 #endif
@@ -80,26 +80,26 @@ CLP(ShaderContext)(Shader *s, GSG *gsg) : ShaderContext(s) {
       dxgsg9_cat.error()
         << "vertex shader cgD3D9LoadProgram failed "
         << D3DERRORSTRING(hr);
-      
+
       CGerror error = cgGetError();
       if (error != CG_NO_ERROR) {
         dxgsg9_cat.error() << "  CG ERROR: " << cgGetErrorString(error) << "\n";
       }
       success = false;
     }
-    
+
     hr = cgD3D9LoadProgram(_cg_fprogram, paramater_shadowing, assembly_flags);
     if (FAILED (hr)) {
       dxgsg9_cat.error()
         << "pixel shader cgD3D9LoadProgram failed "
         << D3DERRORSTRING(hr);
-      
+
       CGerror error = cgGetError();
       if (error != CG_NO_ERROR) {
         dxgsg9_cat.error() << "  CG ERROR: " << cgGetErrorString(error) << "\n";
       }
       success = false;
-    }    
+    }
 
     // BEGIN CG2 CHANGE
     if (_cg_gprogram != 0)
@@ -150,7 +150,7 @@ CLP(ShaderContext)::
 // {
 //   int state;
 //   int file_handle;
-// 
+//
 //   state = false;
 //   file_handle = _open (file_path, _O_CREAT | _O_RDWR | _O_TRUNC, _S_IREAD | _S_IWRITE);
 //   if (file_handle != -1) {
@@ -159,36 +159,36 @@ CLP(ShaderContext)::
 //     }
 //     _close (file_handle);
 //   }
-// 
+//
 //   return state;
 // }
-// 
+//
 //   if (dxgsg9_cat.is_debug()) {
 //     // DEBUG: output the generated program
 //     const char *vertex_program;
 //     const char *pixel_program;
-// 
+//
 //     vertex_program = cgGetProgramString (_cg_program[0], CG_COMPILED_PROGRAM);
 //     pixel_program = cgGetProgramString (_cg_program[1], CG_COMPILED_PROGRAM);
-// 
+//
 //     dxgsg9_cat.debug() << vertex_program << "\n";
 //     dxgsg9_cat.debug() << pixel_program << "\n";
-// 
+//
 //     // save the generated program to a file
 //     int size;
 //     char file_path [512];
-// 
+//
 //     char drive[_MAX_DRIVE];
 //     char dir[_MAX_DIR];
 //     char fname[_MAX_FNAME];
 //     char ext[_MAX_EXT];
-// 
+//
 //     _splitpath (_name.c_str ( ), drive, dir, fname, ext);
-// 
+//
 //     size = strlen (vertex_program);
 //     sprintf (file_path, "%s.vasm", fname);
 //     save_file (size, (void *) vertex_program, file_path);
-// 
+//
 //     size = strlen (pixel_program);
 //     sprintf (file_path, "%s.pasm", fname);
 //     save_file (size, (void *) pixel_program, file_path);
@@ -234,36 +234,36 @@ bind(GSG *gsg) {
 #ifdef HAVE_CG
   if (_cg_context) {
     // clear the last cached FVF to make sure the next SetFVF call goes through
-                                                           
+
     gsg -> _last_fvf = 0;
 
     // Pass in k-parameters and transform-parameters
     issue_parameters(gsg, Shader::SSD_general);
-    
+
     HRESULT hr;
-    
+
     // Bind the shaders.
     bind_state = true;
     hr = cgD3D9BindProgram(_cg_vprogram);
     if (FAILED (hr)) {
       dxgsg9_cat.error() << "cgD3D9BindProgram vertex shader failed " << D3DERRORSTRING(hr);
-      
+
       CGerror error = cgGetError();
       if (error != CG_NO_ERROR) {
         dxgsg9_cat.error() << "  CG ERROR: " << cgGetErrorString(error) << "\n";
       }
-      
+
       bind_state = false;
     }
     hr = cgD3D9BindProgram(_cg_fprogram);
     if (FAILED (hr)) {
       dxgsg9_cat.error() << "cgD3D9BindProgram pixel shader failed " << D3DERRORSTRING(hr);
-      
+
       CGerror error = cgGetError();
       if (error != CG_NO_ERROR) {
         dxgsg9_cat.error() << "  CG ERROR: " << cgGetErrorString(error) << "\n";
       }
-      
+
       bind_state = false;
     }
 
@@ -349,25 +349,25 @@ issue_parameters(GSG *gsg, int altered)
       if(altered & (_shader->_ptr_spec[i]._dep[0] | _shader->_ptr_spec[i]._dep[1])){
 #ifdef HAVE_CG
         const Shader::ShaderPtrSpec& _ptr = _shader->_ptr_spec[i];
-        Shader::ShaderPtrData* _ptr_data = 
+        Shader::ShaderPtrData* _ptr_data =
           const_cast< Shader::ShaderPtrData*>(gsg->fetch_ptr_parameter(_ptr));
-        
+
         if (_ptr_data == NULL){ //the input is not contained in ShaderPtrData
           release_resources();
           return;
         }
 
         CGparameter p = _cg_parameter_map[_ptr._id._seqno];
-        
+
         switch(_ptr_data->_type) {
         case Shader::SPT_float:
-          cgD3D9SetUniform(p, (PN_stdfloat*)_ptr_data->_ptr); 
+          cgD3D9SetUniform(p, (PN_stdfloat*)_ptr_data->_ptr);
           break;
 
-        default: 
-          dxgsg9_cat.error() 
-            << _ptr._id._name << ":" << "unrecognized parameter type\n"; 
-          release_resources(); 
+        default:
+          dxgsg9_cat.error()
+            << _ptr._id._name << ":" << "unrecognized parameter type\n";
+          release_resources();
           return;
         }
       }
@@ -379,7 +379,7 @@ issue_parameters(GSG *gsg, int altered)
         CGparameter p = _cg_parameter_map[_shader->_mat_spec[i]._id._seqno];
         if (p == NULL) {
           continue;
-        }        
+        }
         const LMatrix4 *val = gsg->fetch_specified_value(_shader->_mat_spec[i], altered);
         if (val) {
           HRESULT hr;
@@ -588,7 +588,7 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg, bool force) {
           dxgsg9_cat.info() << "Geometry contains no data for shader parameter " << *name << "\n";
           continue;
         }
-        
+
         // If not associated with the array we're working on, move on.
         if ( param_array_reader != array_reader ) {
           continue;
@@ -768,7 +768,7 @@ disable_shader_texture_bindings(GSG *gsg)
       CGparameter p = _cg_parameter_map[_shader->_tex_spec[i]._id._seqno];
       if (p == NULL) {
         continue;
-      }        
+      }
       int texunit = cgGetParameterResourceIndex(p);
 
       HRESULT hr;
@@ -809,13 +809,17 @@ update_shader_texture_bindings(CLP(ShaderContext) *prev, GSG *gsg)
       CGparameter p = _cg_parameter_map[_shader->_tex_spec[i]._id._seqno];
       if (p == NULL) {
         continue;
-      }        
-      Texture *tex = 0;
+      }
+      Texture *tex = NULL;
       int view = gsg->get_current_tex_view_offset();
       InternalName *id = _shader->_tex_spec[i]._name;
-      if (id != 0) {
+      SamplerState sampler;
+
+      if (id != NULL) {
         const ShaderInput *input = gsg->_target_shader->get_shader_input(id);
         tex = input->get_texture();
+        sampler = input->get_sampler();
+
       } else {
         // We get the TextureAttrib directly from the _target_rs, not the
         // filtered TextureAttrib in _target_texture.
@@ -827,6 +831,7 @@ update_shader_texture_bindings(CLP(ShaderContext) *prev, GSG *gsg)
         }
         TextureStage *stage = texattrib->get_on_stage(_shader->_tex_spec[i]._stage);
         tex = texattrib->get_on_texture(stage);
+        sampler = texattrib->get_on_sampler(stage);
         view += stage->get_tex_view_offset();
       }
       if (_shader->_tex_spec[i]._suffix != 0) {
@@ -843,10 +848,10 @@ update_shader_texture_bindings(CLP(ShaderContext) *prev, GSG *gsg)
       if (tc == (TextureContext*)NULL) {
         continue;
       }
-      
+
       int texunit = cgGetParameterResourceIndex(p);
-      
-      gsg->apply_texture(texunit, tc);
+
+      gsg->apply_texture(texunit, tc, sampler);
     }
   }
 #endif

+ 28 - 28
panda/src/dxgsg9/dxTextureContext9.cxx

@@ -645,21 +645,21 @@ create_texture(DXScreenData &scrn) {
   // validate magfilter setting
   // degrade filtering if no HW support
 
-  Texture::FilterType ft;
+  SamplerState::FilterType ft;
 
   ft = tex->get_magfilter();
-  if ((ft != Texture::FT_linear) && ft != Texture::FT_nearest) {
+  if ((ft != SamplerState::FT_linear) && ft != SamplerState::FT_nearest) {
     // mipmap settings make no sense for magfilter
-    if (ft == Texture::FT_nearest_mipmap_nearest) {
-      ft = Texture::FT_nearest;
+    if (ft == SamplerState::FT_nearest_mipmap_nearest) {
+      ft = SamplerState::FT_nearest;
     } else {
-      ft = Texture::FT_linear;
+      ft = SamplerState::FT_linear;
     }
   }
 
-  if (ft == Texture::FT_linear &&
+  if (ft == SamplerState::FT_linear &&
       (filter_caps & D3DPTFILTERCAPS_MAGFLINEAR) == 0) {
-    ft = Texture::FT_nearest;
+    ft = SamplerState::FT_nearest;
   }
   tex->set_magfilter(ft);
 
@@ -669,33 +669,33 @@ create_texture(DXScreenData &scrn) {
 
   if (!dx_ignore_mipmaps) {  // set if no HW mipmap capable
     switch(ft) {
-    case Texture::FT_nearest_mipmap_nearest:
-    case Texture::FT_linear_mipmap_nearest:
-    case Texture::FT_nearest_mipmap_linear:  // pick nearest in each, interpolate linearly b/w them
-    case Texture::FT_linear_mipmap_linear:
+    case SamplerState::FT_nearest_mipmap_nearest:
+    case SamplerState::FT_linear_mipmap_nearest:
+    case SamplerState::FT_nearest_mipmap_linear:  // pick nearest in each, interpolate linearly b/w them
+    case SamplerState::FT_linear_mipmap_linear:
       _has_mipmaps = true;
     }
 
     if (dx_mipmap_everything) {  // debug toggle, ok to leave in since its just a creation cost
       _has_mipmaps = true;
       if (dxgsg9_cat.is_spam()) {
-        if (ft != Texture::FT_linear_mipmap_linear) {
+        if (ft != SamplerState::FT_linear_mipmap_linear) {
           dxgsg9_cat.spam()
             << "Forcing trilinear mipmapping on DX texture ["
             << tex->get_name() << "]\n";
         }
       }
-      ft = Texture::FT_linear_mipmap_linear;
+      ft = SamplerState::FT_linear_mipmap_linear;
       tex->set_minfilter(ft);
     }
 
-  } else if ((ft == Texture::FT_nearest_mipmap_nearest) ||   // cvt to no-mipmap filter types
-             (ft == Texture::FT_nearest_mipmap_linear)) {
-    ft = Texture::FT_nearest;
+  } else if ((ft == SamplerState::FT_nearest_mipmap_nearest) ||   // cvt to no-mipmap filter types
+             (ft == SamplerState::FT_nearest_mipmap_linear)) {
+    ft = SamplerState::FT_nearest;
 
-  } else if ((ft == Texture::FT_linear_mipmap_nearest) ||
-            (ft == Texture::FT_linear_mipmap_linear)) {
-    ft = Texture::FT_linear;
+  } else if ((ft == SamplerState::FT_linear_mipmap_nearest) ||
+            (ft == SamplerState::FT_linear_mipmap_linear)) {
+    ft = SamplerState::FT_linear;
   }
 
   nassertr((filter_caps & D3DPTFILTERCAPS_MINFPOINT) != 0, false);
@@ -704,36 +704,36 @@ create_texture(DXScreenData &scrn) {
 
   // do any other filter type degradations necessary
   switch(ft) {
-  case Texture::FT_linear_mipmap_linear:
+  case SamplerState::FT_linear_mipmap_linear:
     if ((filter_caps & TRILINEAR_MIPMAP_TEXFILTERCAPS) != TRILINEAR_MIPMAP_TEXFILTERCAPS) {
       if (filter_caps & D3DPTFILTERCAPS_MINFLINEAR) {
-        ft = Texture::FT_linear_mipmap_nearest;
+        ft = SamplerState::FT_linear_mipmap_nearest;
       } else {
         // if you cant do linear in a level, you probably cant do
         // linear b/w levels, so just do nearest-all
-        ft = Texture::FT_nearest_mipmap_nearest;
+        ft = SamplerState::FT_nearest_mipmap_nearest;
       }
     }
     break;
 
-  case Texture::FT_nearest_mipmap_linear:
+  case SamplerState::FT_nearest_mipmap_linear:
     // if we don't have bilinear, do nearest_nearest
     if (!((filter_caps & D3DPTFILTERCAPS_MIPFPOINT) &&
           (filter_caps & D3DPTFILTERCAPS_MINFLINEAR))) {
-      ft = Texture::FT_nearest_mipmap_nearest;
+      ft = SamplerState::FT_nearest_mipmap_nearest;
     }
     break;
 
-  case Texture::FT_linear_mipmap_nearest:
+  case SamplerState::FT_linear_mipmap_nearest:
     // if we don't have mip linear, do nearest_nearest
     if (!(filter_caps & D3DPTFILTERCAPS_MIPFLINEAR)) {
-      ft = Texture::FT_nearest_mipmap_nearest;
+      ft = SamplerState::FT_nearest_mipmap_nearest;
     }
     break;
 
-  case Texture::FT_linear:
+  case SamplerState::FT_linear:
     if (!(filter_caps & D3DPTFILTERCAPS_MINFLINEAR)) {
-      ft = Texture::FT_nearest;
+      ft = SamplerState::FT_nearest;
     }
     break;
   }

+ 27 - 27
panda/src/egg2pg/eggLoader.cxx

@@ -1108,16 +1108,16 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
 
   switch (egg_tex->get_minfilter()) {
   case EggTexture::FT_nearest:
-    tex->set_minfilter(Texture::FT_nearest);
+    tex->set_minfilter(SamplerState::FT_nearest);
     break;
 
   case EggTexture::FT_linear:
     if (egg_ignore_filters) {
       egg2pg_cat.warning()
         << "Ignoring minfilter request\n";
-      tex->set_minfilter(Texture::FT_nearest);
+      tex->set_minfilter(SamplerState::FT_nearest);
     } else {
-      tex->set_minfilter(Texture::FT_linear);
+      tex->set_minfilter(SamplerState::FT_linear);
     }
     break;
 
@@ -1125,13 +1125,13 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
     if (egg_ignore_filters) {
       egg2pg_cat.warning()
         << "Ignoring minfilter request\n";
-      tex->set_minfilter(Texture::FT_nearest);
+      tex->set_minfilter(SamplerState::FT_nearest);
     } else if (egg_ignore_mipmaps) {
       egg2pg_cat.warning()
         << "Ignoring mipmap request\n";
-      tex->set_minfilter(Texture::FT_nearest);
+      tex->set_minfilter(SamplerState::FT_nearest);
     } else {
-      tex->set_minfilter(Texture::FT_nearest_mipmap_nearest);
+      tex->set_minfilter(SamplerState::FT_nearest_mipmap_nearest);
     }
     break;
 
@@ -1139,13 +1139,13 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
     if (egg_ignore_filters) {
       egg2pg_cat.warning()
         << "Ignoring minfilter request\n";
-      tex->set_minfilter(Texture::FT_nearest);
+      tex->set_minfilter(SamplerState::FT_nearest);
     } else if (egg_ignore_mipmaps) {
       egg2pg_cat.warning()
         << "Ignoring mipmap request\n";
-      tex->set_minfilter(Texture::FT_linear);
+      tex->set_minfilter(SamplerState::FT_linear);
     } else {
-      tex->set_minfilter(Texture::FT_linear_mipmap_nearest);
+      tex->set_minfilter(SamplerState::FT_linear_mipmap_nearest);
     }
     break;
 
@@ -1153,13 +1153,13 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
     if (egg_ignore_filters) {
       egg2pg_cat.warning()
         << "Ignoring minfilter request\n";
-      tex->set_minfilter(Texture::FT_nearest);
+      tex->set_minfilter(SamplerState::FT_nearest);
     } else if (egg_ignore_mipmaps) {
       egg2pg_cat.warning()
         << "Ignoring mipmap request\n";
-      tex->set_minfilter(Texture::FT_nearest);
+      tex->set_minfilter(SamplerState::FT_nearest);
     } else {
-      tex->set_minfilter(Texture::FT_nearest_mipmap_linear);
+      tex->set_minfilter(SamplerState::FT_nearest_mipmap_linear);
     }
     break;
 
@@ -1167,13 +1167,13 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
     if (egg_ignore_filters) {
       egg2pg_cat.warning()
         << "Ignoring minfilter request\n";
-      tex->set_minfilter(Texture::FT_nearest);
+      tex->set_minfilter(SamplerState::FT_nearest);
     } else if (egg_ignore_mipmaps) {
       egg2pg_cat.warning()
         << "Ignoring mipmap request\n";
-      tex->set_minfilter(Texture::FT_linear);
+      tex->set_minfilter(SamplerState::FT_linear);
     } else {
-      tex->set_minfilter(Texture::FT_linear_mipmap_linear);
+      tex->set_minfilter(SamplerState::FT_linear_mipmap_linear);
     }
     break;
 
@@ -1185,7 +1185,7 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
   case EggTexture::FT_nearest:
   case EggTexture::FT_nearest_mipmap_nearest:
   case EggTexture::FT_nearest_mipmap_linear:
-    tex->set_magfilter(Texture::FT_nearest);
+    tex->set_magfilter(SamplerState::FT_nearest);
     break;
 
   case EggTexture::FT_linear:
@@ -1194,9 +1194,9 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
     if (egg_ignore_filters) {
       egg2pg_cat.warning()
         << "Ignoring magfilter request\n";
-      tex->set_magfilter(Texture::FT_nearest);
+      tex->set_magfilter(SamplerState::FT_nearest);
     } else {
-      tex->set_magfilter(Texture::FT_linear);
+      tex->set_magfilter(SamplerState::FT_linear);
     }
     break;
 
@@ -1397,35 +1397,35 @@ convert_compression_mode(EggTexture::CompressionMode compression_mode) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::convert_wrap_mode
 //       Access: Private
-//  Description: Returns the Texture::WrapMode enum corresponding to
+//  Description: Returns the SamplerState::WrapMode enum corresponding to
 //               the EggTexture::WrapMode.  Returns WM_repeat if the
 //               wrap mode is unspecified.
 ////////////////////////////////////////////////////////////////////
-Texture::WrapMode EggLoader::
+SamplerState::WrapMode EggLoader::
 convert_wrap_mode(EggTexture::WrapMode wrap_mode) const {
   switch (wrap_mode) {
   case EggTexture::WM_clamp:
-    return Texture::WM_clamp;
+    return SamplerState::WM_clamp;
 
   case EggTexture::WM_repeat:
-    return Texture::WM_repeat;
+    return SamplerState::WM_repeat;
 
   case EggTexture::WM_mirror:
-    return Texture::WM_mirror;
+    return SamplerState::WM_mirror;
 
   case EggTexture::WM_mirror_once:
-    return Texture::WM_mirror_once;
+    return SamplerState::WM_mirror_once;
 
   case EggTexture::WM_border_color:
-    return Texture::WM_border_color;
+    return SamplerState::WM_border_color;
 
   case EggTexture::WM_unspecified:
-    return Texture::WM_repeat;
+    return SamplerState::WM_repeat;
   }
 
   egg2pg_cat.warning()
     << "Unexpected texture wrap flag: " << (int)wrap_mode << "\n";
-  return Texture::WM_repeat;
+  return SamplerState::WM_repeat;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/egg2pg/eggLoader.h

@@ -123,7 +123,7 @@ private:
   bool load_texture(TextureDef &def, EggTexture *egg_tex);
   void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex);
   Texture::CompressionMode convert_compression_mode(EggTexture::CompressionMode compression_mode) const;
-  Texture::WrapMode convert_wrap_mode(EggTexture::WrapMode wrap_mode) const;
+  SamplerState::WrapMode convert_wrap_mode(EggTexture::WrapMode wrap_mode) const;
   PT(TextureStage) make_texture_stage(const EggTexture *egg_tex);
 
   void separate_switches(EggNode *egg_node);

+ 12 - 12
panda/src/egg2pg/eggSaver.cxx

@@ -965,22 +965,22 @@ get_egg_texture(Texture *tex) {
       }
 
       switch (tex->get_minfilter()) {
-      case Texture::FT_nearest:
+      case SamplerState::FT_nearest:
         temp.set_minfilter(EggTexture::FT_nearest);
         break;
-      case Texture::FT_linear:
+      case SamplerState::FT_linear:
         temp.set_minfilter(EggTexture::FT_linear);
         break;
-      case Texture::FT_nearest_mipmap_nearest:
+      case SamplerState::FT_nearest_mipmap_nearest:
         temp.set_minfilter(EggTexture::FT_nearest_mipmap_nearest);
         break;
-      case Texture::FT_linear_mipmap_nearest:
+      case SamplerState::FT_linear_mipmap_nearest:
         temp.set_minfilter(EggTexture::FT_linear_mipmap_nearest);
         break;
-      case Texture::FT_nearest_mipmap_linear:
+      case SamplerState::FT_nearest_mipmap_linear:
         temp.set_minfilter(EggTexture::FT_nearest_mipmap_linear);
         break;
-      case Texture::FT_linear_mipmap_linear:
+      case SamplerState::FT_linear_mipmap_linear:
         temp.set_minfilter(EggTexture::FT_linear_mipmap_linear);
         break;
 
@@ -989,10 +989,10 @@ get_egg_texture(Texture *tex) {
       }
 
       switch (tex->get_magfilter()) {
-      case Texture::FT_nearest:
+      case SamplerState::FT_nearest:
         temp.set_magfilter(EggTexture::FT_nearest);
         break;
-      case Texture::FT_linear:
+      case SamplerState::FT_linear:
         temp.set_magfilter(EggTexture::FT_linear);
         break;
 
@@ -1001,10 +1001,10 @@ get_egg_texture(Texture *tex) {
       }
 
       switch (tex->get_wrap_u()) {
-      case Texture::WM_clamp:
+      case SamplerState::WM_clamp:
         temp.set_wrap_u(EggTexture::WM_clamp);
         break;
-      case Texture::WM_repeat:
+      case SamplerState::WM_repeat:
         temp.set_wrap_u(EggTexture::WM_repeat);
         break;
 
@@ -1015,10 +1015,10 @@ get_egg_texture(Texture *tex) {
       }
 
       switch (tex->get_wrap_v()) {
-      case Texture::WM_clamp:
+      case SamplerState::WM_clamp:
         temp.set_wrap_v(EggTexture::WM_clamp);
         break;
-      case Texture::WM_repeat:
+      case SamplerState::WM_repeat:
         temp.set_wrap_v(EggTexture::WM_repeat);
         break;
 

+ 4 - 4
panda/src/framework/windowFramework.cxx

@@ -710,8 +710,8 @@ load_default_model(const NodePath &parent) {
     PT(Texture) tex = new Texture;
     tex->set_name("rock-floor.rgb");
     tex->load(rock_floor_pnm);
-    tex->set_minfilter(Texture::FT_linear);
-    tex->set_magfilter(Texture::FT_linear);
+    tex->set_minfilter(SamplerState::FT_linear);
+    tex->set_magfilter(SamplerState::FT_linear);
     state = state->add_attrib(TextureAttrib::make(tex));
   }
 
@@ -1257,8 +1257,8 @@ load_image_as_model(const Filename &filename) {
   }
 
   // Yes, it is an image file; make a texture out of it.
-  tex->set_minfilter(Texture::FT_linear_mipmap_linear);
-  tex->set_magfilter(Texture::FT_linear);
+  tex->set_minfilter(SamplerState::FT_linear_mipmap_linear);
+  tex->set_magfilter(SamplerState::FT_linear);
 
   // Ok, now make a polygon to show the texture.
   bool has_alpha = true;

+ 9 - 2
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -570,19 +570,25 @@ update_shader_texture_bindings(ShaderContext *prev) {
     }
     int texunit = cgGetParameterResourceIndex(p);
 
-    Texture *tex = 0;
+    Texture *tex = NULL;
     int view = _glgsg->get_current_tex_view_offset();
-    if (id != 0) {
+    SamplerState sampler;
+
+    if (id != NULL) {
       const ShaderInput *input = _glgsg->_target_shader->get_shader_input(id);
       tex = input->get_texture();
+      sampler = input->get_sampler();
+
     } else {
       if (_shader->_tex_spec[i]._stage >= texattrib->get_num_on_stages()) {
         continue;
       }
       TextureStage *stage = texattrib->get_on_stage(_shader->_tex_spec[i]._stage);
       tex = texattrib->get_on_texture(stage);
+      sampler = texattrib->get_on_sampler(stage);
       view += stage->get_tex_view_offset();
     }
+
     if (_shader->_tex_spec[i]._suffix != 0) {
       // The suffix feature is inefficient. It is a temporary hack.
       if (tex == 0) {
@@ -612,6 +618,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
     }
 
     _glgsg->apply_texture(tc);
+    _glgsg->apply_sampler(i, sampler, tc);
   }
 
   cg_report_errors();

+ 259 - 64
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -64,6 +64,7 @@
 #include "stencilAttrib.h"
 #include "graphicsEngine.h"
 #include "shaderGenerator.h"
+#include "samplerState.h"
 
 #if defined(HAVE_CG) && !defined(OPENGLES)
 #include "Cg/cgGL.h"
@@ -1870,6 +1871,28 @@ reset() {
     _glMemoryBarrier = NULL;
   }
 
+  _supports_sampler_objects = false;
+  if (gl_support_sampler_objects &&
+      ((is_at_least_gl_version(3, 3) || has_extension("GL_ARB_sampler_objects")))) {
+    _glGenSamplers = (PFNGLGENSAMPLERSPROC) get_extension_func("glGenSamplers");
+    _glDeleteSamplers = (PFNGLDELETESAMPLERSPROC) get_extension_func("glDeleteSamplers");
+    _glBindSampler = (PFNGLBINDSAMPLERPROC) get_extension_func("glBindSampler");
+    _glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) get_extension_func("glSamplerParameteri");
+    _glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) get_extension_func("glSamplerParameteriv");
+    _glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) get_extension_func("glSamplerParameterf");
+    _glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) get_extension_func("glSamplerParameterfv");
+
+    if (_glGenSamplers == NULL || _glDeleteSamplers == NULL ||
+        _glBindSampler == NULL || _glSamplerParameteri == NULL ||
+        _glSamplerParameteriv == NULL || _glSamplerParameterf == NULL ||
+        _glSamplerParameterfv == NULL) {
+      GLCAT.warning()
+        << "GL_ARB_sampler_objects advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
+    } else {
+      _supports_sampler_objects = true;
+    }
+  }
+
   // Check availability of multi-bind functions.
   _supports_multi_bind = false;
   if (is_at_least_gl_version(4, 4) || has_extension("GL_ARB_multi_bind")) {
@@ -1878,6 +1901,11 @@ reset() {
     _glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)
       get_extension_func("glBindImageTextures");
 
+    if (_supports_sampler_objects) {
+      _glBindSamplers = (PFNGLBINDSAMPLERSPROC)
+        get_extension_func("glBindSamplers");
+    }
+
     if (_glBindTextures != NULL && _glBindImageTextures != NULL) {
       _supports_multi_bind = true;
     } else {
@@ -1900,6 +1928,8 @@ reset() {
   if (has_extension("GL_ARB_bindless_texture")) {
     _glGetTextureHandle = (PFNGLGETTEXTUREHANDLEPROC)
       get_extension_func("glGetTextureHandleARB");
+    _glGetTextureSamplerHandle = (PFNGLGETTEXTURESAMPLERHANDLEPROC)
+      get_extension_func("glGetTextureSamplerHandleARB");
     _glMakeTextureHandleResident = (PFNGLMAKETEXTUREHANDLERESIDENTPROC)
       get_extension_func("glMakeTextureHandleResidentARB");
     _glUniformHandleui64 = (PFNGLUNIFORMHANDLEUI64PROC)
@@ -2200,7 +2230,6 @@ reset() {
   add_gsg(this);
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::finish
 //       Access: Public, Virtual
@@ -2517,6 +2546,12 @@ clear_before_callback() {
   // texture stage is still set to stage 0.  CEGUI, in particular,
   // makes this assumption.
   _glActiveTexture(GL_TEXTURE0);
+
+  // Clear the bound sampler object, so that we do not inadvertently
+  // override the callback's desired sampler settings.
+  if (_supports_sampler_objects) {
+    _glBindSampler(0, 0);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -4234,7 +4269,8 @@ update_texture(TextureContext *tc, bool force) {
     // If the texture image was modified, reload the texture.
     apply_texture(tc);
     if (gtc->was_properties_modified()) {
-      specify_texture(gtc);
+      Texture *tex = tc->get_texture();
+      specify_texture(gtc, tex->get_default_sampler());
     }
     bool okflag = upload_texture(gtc, force);
     if (!okflag) {
@@ -4249,13 +4285,15 @@ update_texture(TextureContext *tc, bool force) {
     // If only the properties have been modified, we don't necessarily
     // need to reload the texture.
     apply_texture(tc);
-    if (specify_texture(gtc)) {
+
+    Texture *tex = tc->get_texture();
+    if (specify_texture(gtc, tex->get_default_sampler())) {
       // Actually, looks like the texture *does* need to be reloaded.
       gtc->mark_needs_reload();
       bool okflag = upload_texture(gtc, force);
       if (!okflag) {
         GLCAT.error()
-          << "Could not load " << *gtc->get_texture() << "\n";
+          << "Could not load " << *tex << "\n";
         return false;
       }
 
@@ -4318,6 +4356,109 @@ extract_texture_data(Texture *tex) {
   return success;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::prepare_sampler
+//       Access: Public, Virtual
+//  Description: Creates whatever structures the GSG requires to
+//               represent the sampler state internally, and returns a
+//               newly-allocated SamplerContext object with this data.
+//               It is the responsibility of the calling function to
+//               later call release_sampler() with this same pointer
+//               (which will also delete the pointer).
+//
+//               This function should not be called directly to
+//               prepare a sampler object.  Instead, call
+//               SamplerState::prepare().
+////////////////////////////////////////////////////////////////////
+SamplerContext *CLP(GraphicsStateGuardian)::
+prepare_sampler(const SamplerState &sampler) {
+#ifndef OPENGLES
+  nassertr(_supports_sampler_objects, NULL);
+  PStatGPUTimer timer(this, _prepare_sampler_pcollector);
+
+  CLP(SamplerContext) *gsc = new CLP(SamplerContext)(this, sampler);
+  GLuint index = gsc->_index;
+
+  // Sampler contexts are immutable in Panda, so might as well just
+  // initialize all the settings here.
+  _glSamplerParameteri(index, GL_TEXTURE_WRAP_S,
+                       get_texture_wrap_mode(sampler.get_wrap_u()));
+  _glSamplerParameteri(index, GL_TEXTURE_WRAP_T,
+                       get_texture_wrap_mode(sampler.get_wrap_v()));
+  _glSamplerParameteri(index, GL_TEXTURE_WRAP_R,
+                       get_texture_wrap_mode(sampler.get_wrap_w()));
+
+#ifdef STDFLOAT_DOUBLE
+  LVecBase4f fvalue = LCAST(float, sampler.get_border_color());
+  _glSamplerParameterfv(index, GL_TEXTURE_BORDER_COLOR, fvalue.get_data());
+#else
+  _glSamplerParameterfv(index, GL_TEXTURE_BORDER_COLOR,
+                        sampler.get_border_color().get_data());
+#endif
+
+  SamplerState::FilterType minfilter = sampler.get_effective_minfilter();
+  SamplerState::FilterType magfilter = sampler.get_effective_magfilter();
+  bool uses_mipmaps = Texture::is_mipmap(minfilter) && !gl_ignore_mipmaps;
+
+#ifndef NDEBUG
+  if (gl_force_mipmaps) {
+    minfilter = SamplerState::FT_linear_mipmap_linear;
+    magfilter = SamplerState::FT_linear;
+    uses_mipmaps = true;
+  }
+#endif
+
+  _glSamplerParameteri(index, GL_TEXTURE_MIN_FILTER,
+                              get_texture_filter_type(minfilter, !uses_mipmaps));
+  _glSamplerParameteri(index, GL_TEXTURE_MAG_FILTER,
+                              get_texture_filter_type(magfilter, true));
+
+  // Set anisotropic filtering.
+  if (_supports_anisotropy) {
+    PN_stdfloat anisotropy = sampler.get_effective_anisotropic_degree();
+    anisotropy = min(anisotropy, _max_anisotropy);
+    anisotropy = max(anisotropy, (PN_stdfloat)1.0);
+    _glSamplerParameterf(index, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
+  }
+
+  if (_supports_shadow_filter) {
+    if ((sampler.get_magfilter() == SamplerState::FT_shadow) ||
+        (sampler.get_minfilter() == SamplerState::FT_shadow)) {
+      _glSamplerParameteri(index, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
+      _glSamplerParameteri(index, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
+    } else {
+      _glSamplerParameteri(index, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
+      _glSamplerParameteri(index, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
+    }
+  }
+
+  _glSamplerParameterf(index, GL_TEXTURE_MIN_LOD, sampler.get_min_lod());
+  _glSamplerParameterf(index, GL_TEXTURE_MAX_LOD, sampler.get_max_lod());
+  _glSamplerParameterf(index, GL_TEXTURE_LOD_BIAS, sampler.get_lod_bias());
+
+  gsc->enqueue_lru(&_prepared_objects->_sampler_object_lru);
+
+  report_my_gl_errors();
+  return gsc;
+
+#else
+  return NULL;
+#endif // OPENGLES
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::release_sampler
+//       Access: Public, Virtual
+//  Description: Frees the GL resources previously allocated for the
+//               sampler.  This function should never be called
+//               directly; instead, call SamplerState::release().
+////////////////////////////////////////////////////////////////////
+void CLP(GraphicsStateGuardian)::
+release_sampler(SamplerContext *sc) {
+  CLP(SamplerContext) *gsc = DCAST(CLP(SamplerContext), sc);
+  delete gsc;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::prepare_geom
 //       Access: Public, Virtual
@@ -5095,7 +5236,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
   CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
 
   apply_texture(gtc);
-  bool needs_reload = specify_texture(gtc);
+  bool needs_reload = specify_texture(gtc, tex->get_default_sampler());
 
   GLenum target = get_texture_target(tex->get_texture_type());
   GLint internal_format = get_internal_image_format(tex);
@@ -6964,30 +7105,30 @@ get_texture_target(Texture::TextureType texture_type) const {
 //               GL's.
 ////////////////////////////////////////////////////////////////////
 GLenum CLP(GraphicsStateGuardian)::
-get_texture_wrap_mode(Texture::WrapMode wm) const {
+get_texture_wrap_mode(SamplerState::WrapMode wm) const {
   if (gl_ignore_clamp) {
     return GL_REPEAT;
   }
   switch (wm) {
-  case Texture::WM_clamp:
+  case SamplerState::WM_clamp:
     return _edge_clamp;
 
-  case Texture::WM_repeat:
+  case SamplerState::WM_repeat:
     return GL_REPEAT;
 
-  case Texture::WM_mirror:
+  case SamplerState::WM_mirror:
     return _mirror_repeat;
 
-  case Texture::WM_mirror_once:
+  case SamplerState::WM_mirror_once:
     return _mirror_border_clamp;
 
-  case Texture::WM_border_color:
+  case SamplerState::WM_border_color:
     return _border_clamp;
 
-  case Texture::WM_invalid:
+  case SamplerState::WM_invalid:
     break;
   }
-  GLCAT.error() << "Invalid Texture::WrapMode value!\n";
+  GLCAT.error() << "Invalid SamplerState::WrapMode value!\n";
   return _edge_clamp;
 }
 
@@ -6997,34 +7138,34 @@ get_texture_wrap_mode(Texture::WrapMode wm) const {
 //  Description: Maps from the GL's internal wrap mode symbols to
 //               Panda's.
 ////////////////////////////////////////////////////////////////////
-Texture::WrapMode CLP(GraphicsStateGuardian)::
+SamplerState::WrapMode CLP(GraphicsStateGuardian)::
 get_panda_wrap_mode(GLenum wm) {
   switch (wm) {
 #ifndef OPENGLES
   case GL_CLAMP:
 #endif
   case GL_CLAMP_TO_EDGE:
-    return Texture::WM_clamp;
+    return SamplerState::WM_clamp;
 
 #ifndef OPENGLES
   case GL_CLAMP_TO_BORDER:
-    return Texture::WM_border_color;
+    return SamplerState::WM_border_color;
 #endif
 
   case GL_REPEAT:
-    return Texture::WM_repeat;
+    return SamplerState::WM_repeat;
 
 #ifndef OPENGLES
   case GL_MIRROR_CLAMP_EXT:
   case GL_MIRROR_CLAMP_TO_EDGE_EXT:
-    return Texture::WM_mirror;
+    return SamplerState::WM_mirror;
 
   case GL_MIRROR_CLAMP_TO_BORDER_EXT:
-    return Texture::WM_mirror_once;
+    return SamplerState::WM_mirror_once;
 #endif
   }
   GLCAT.error() << "Unexpected GL wrap mode " << (int)wm << "\n";
-  return Texture::WM_clamp;
+  return SamplerState::WM_clamp;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -7034,49 +7175,49 @@ get_panda_wrap_mode(GLenum wm) {
 //               to GL's.
 ////////////////////////////////////////////////////////////////////
 GLenum CLP(GraphicsStateGuardian)::
-get_texture_filter_type(Texture::FilterType ft, bool ignore_mipmaps) {
+get_texture_filter_type(SamplerState::FilterType ft, bool ignore_mipmaps) {
   if (gl_ignore_filters) {
     return GL_NEAREST;
 
   } else if (ignore_mipmaps) {
     switch (ft) {
-    case Texture::FT_nearest_mipmap_nearest:
-    case Texture::FT_nearest:
+    case SamplerState::FT_nearest_mipmap_nearest:
+    case SamplerState::FT_nearest:
       return GL_NEAREST;
-    case Texture::FT_linear:
-    case Texture::FT_linear_mipmap_nearest:
-    case Texture::FT_nearest_mipmap_linear:
-    case Texture::FT_linear_mipmap_linear:
+    case SamplerState::FT_linear:
+    case SamplerState::FT_linear_mipmap_nearest:
+    case SamplerState::FT_nearest_mipmap_linear:
+    case SamplerState::FT_linear_mipmap_linear:
       return GL_LINEAR;
-    case Texture::FT_shadow:
+    case SamplerState::FT_shadow:
       return GL_LINEAR;
-    case Texture::FT_default:
-    case Texture::FT_invalid:
+    case SamplerState::FT_default:
+    case SamplerState::FT_invalid:
       break;
     }
 
   } else {
     switch (ft) {
-    case Texture::FT_nearest:
+    case SamplerState::FT_nearest:
       return GL_NEAREST;
-    case Texture::FT_linear:
+    case SamplerState::FT_linear:
       return GL_LINEAR;
-    case Texture::FT_nearest_mipmap_nearest:
+    case SamplerState::FT_nearest_mipmap_nearest:
       return GL_NEAREST_MIPMAP_NEAREST;
-    case Texture::FT_linear_mipmap_nearest:
+    case SamplerState::FT_linear_mipmap_nearest:
       return GL_LINEAR_MIPMAP_NEAREST;
-    case Texture::FT_nearest_mipmap_linear:
+    case SamplerState::FT_nearest_mipmap_linear:
       return GL_NEAREST_MIPMAP_LINEAR;
-    case Texture::FT_linear_mipmap_linear:
+    case SamplerState::FT_linear_mipmap_linear:
       return GL_LINEAR_MIPMAP_LINEAR;
-    case Texture::FT_shadow:
+    case SamplerState::FT_shadow:
       return GL_LINEAR;
-    case Texture::FT_default:
-    case Texture::FT_invalid:
+    case SamplerState::FT_default:
+    case SamplerState::FT_invalid:
       break;
     }
   }
-  GLCAT.error() << "Invalid Texture::FilterType value!\n";
+  GLCAT.error() << "Invalid SamplerState::FilterType value!\n";
   return GL_NEAREST;
 }
 
@@ -7086,24 +7227,24 @@ get_texture_filter_type(Texture::FilterType ft, bool ignore_mipmaps) {
 //  Description: Maps from the GL's internal filter type symbols
 //               to Panda's.
 ////////////////////////////////////////////////////////////////////
-Texture::FilterType CLP(GraphicsStateGuardian)::
+SamplerState::FilterType CLP(GraphicsStateGuardian)::
 get_panda_filter_type(GLenum ft) {
   switch (ft) {
   case GL_NEAREST:
-    return Texture::FT_nearest;
+    return SamplerState::FT_nearest;
   case GL_LINEAR:
-    return Texture::FT_linear;
+    return SamplerState::FT_linear;
   case GL_NEAREST_MIPMAP_NEAREST:
-    return Texture::FT_nearest_mipmap_nearest;
+    return SamplerState::FT_nearest_mipmap_nearest;
   case GL_LINEAR_MIPMAP_NEAREST:
-    return Texture::FT_linear_mipmap_nearest;
+    return SamplerState::FT_linear_mipmap_nearest;
   case GL_NEAREST_MIPMAP_LINEAR:
-    return Texture::FT_nearest_mipmap_linear;
+    return SamplerState::FT_nearest_mipmap_linear;
   case GL_LINEAR_MIPMAP_LINEAR:
-    return Texture::FT_linear_mipmap_linear;
+    return SamplerState::FT_linear_mipmap_linear;
   }
   GLCAT.error() << "Unexpected GL filter type " << (int)ft << "\n";
-  return Texture::FT_linear;
+  return SamplerState::FT_linear;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -8972,6 +9113,7 @@ update_standard_texture_bindings() {
       continue;
     }
     apply_texture(tc);
+    apply_sampler(i, _target_texture->get_on_sampler(stage), tc);
 
     if (stage->involves_color_scale() && _color_scale_enabled) {
       LColor color = stage->get_color();
@@ -9651,10 +9793,11 @@ do_issue_tex_gen() {
 //     Function: GLGraphicsStateGuardian::specify_texture
 //       Access: Protected
 //  Description: Specifies the texture parameters.  Returns true if
-//               the texture may need to be reloaded.
+//               the texture may need to be reloaded.  Pass non-NULL
+//               sampler argument to use different sampler settings.
 ////////////////////////////////////////////////////////////////////
 bool CLP(GraphicsStateGuardian)::
-specify_texture(CLP(TextureContext) *gtc) {
+specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler) {
   nassertr(gtc->_handle == 0 /* can't modify tex with active handle */, false);
 
   Texture *tex = gtc->get_texture();
@@ -9665,38 +9808,41 @@ specify_texture(CLP(TextureContext) *gtc) {
     return false;
   }
 
+  // Record the active sampler settings.
+  gtc->_active_sampler = sampler;
+
   glTexParameteri(target, GL_TEXTURE_WRAP_S,
-                     get_texture_wrap_mode(tex->get_wrap_u()));
+                  get_texture_wrap_mode(sampler.get_wrap_u()));
 #ifndef OPENGLES
   if (target != GL_TEXTURE_1D) {
     glTexParameteri(target, GL_TEXTURE_WRAP_T,
-                       get_texture_wrap_mode(tex->get_wrap_v()));
+                    get_texture_wrap_mode(sampler.get_wrap_v()));
   }
 #endif
 #ifdef OPENGLES_2
   if (target == GL_TEXTURE_3D_OES) {
     glTexParameteri(target, GL_TEXTURE_WRAP_R_OES,
-                       get_texture_wrap_mode(tex->get_wrap_w()));
+                    get_texture_wrap_mode(sampler.get_wrap_w()));
   }
 #endif
 #ifndef OPENGLES
   if (target == GL_TEXTURE_3D) {
     glTexParameteri(target, GL_TEXTURE_WRAP_R,
-                       get_texture_wrap_mode(tex->get_wrap_w()));
+                    get_texture_wrap_mode(sampler.get_wrap_w()));
   }
 
-  LColor border_color = tex->get_border_color();
+  LColor border_color = sampler.get_border_color();
   call_glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, border_color);
 #endif  // OPENGLES
 
-  Texture::FilterType minfilter = tex->get_effective_minfilter();
-  Texture::FilterType magfilter = tex->get_effective_magfilter();
+  SamplerState::FilterType minfilter = sampler.get_effective_minfilter();
+  SamplerState::FilterType magfilter = sampler.get_effective_magfilter();
   bool uses_mipmaps = Texture::is_mipmap(minfilter) && !gl_ignore_mipmaps;
 
 #ifndef NDEBUG
   if (gl_force_mipmaps) {
-    minfilter = Texture::FT_linear_mipmap_linear;
-    magfilter = Texture::FT_linear;
+    minfilter = SamplerState::FT_linear_mipmap_linear;
+    magfilter = SamplerState::FT_linear;
     uses_mipmaps = true;
   }
 #endif
@@ -9713,13 +9859,13 @@ specify_texture(CLP(TextureContext) *gtc) {
   }
 
   glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
-                     get_texture_filter_type(minfilter, !uses_mipmaps));
+                  get_texture_filter_type(minfilter, !uses_mipmaps));
   glTexParameteri(target, GL_TEXTURE_MAG_FILTER,
-                     get_texture_filter_type(magfilter, true));
+                  get_texture_filter_type(magfilter, true));
 
   // Set anisotropic filtering.
   if (_supports_anisotropy) {
-    PN_stdfloat anisotropy = tex->get_effective_anisotropic_degree();
+    PN_stdfloat anisotropy = sampler.get_effective_anisotropic_degree();
     anisotropy = min(anisotropy, _max_anisotropy);
     anisotropy = max(anisotropy, (PN_stdfloat)1.0);
     glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
@@ -9733,8 +9879,8 @@ specify_texture(CLP(TextureContext) *gtc) {
       tex->get_format() == Texture::F_depth_component32) {
     glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY);
     if (_supports_shadow_filter) {
-      if ((tex->get_magfilter() == Texture::FT_shadow) ||
-          (tex->get_minfilter() == Texture::FT_shadow)) {
+      if ((sampler.get_magfilter() == SamplerState::FT_shadow) ||
+          (sampler.get_minfilter() == SamplerState::FT_shadow)) {
         glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
         glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
       } else {
@@ -9743,6 +9889,10 @@ specify_texture(CLP(TextureContext) *gtc) {
       }
     }
   }
+
+  glTexParameterf(target, GL_TEXTURE_MIN_LOD, sampler.get_min_lod());
+  glTexParameterf(target, GL_TEXTURE_MAX_LOD, sampler.get_max_lod());
+  glTexParameterf(target, GL_TEXTURE_LOD_BIAS, sampler.get_lod_bias());
 #endif
 
   report_my_gl_errors();
@@ -9785,6 +9935,51 @@ apply_texture(TextureContext *tc) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::apply_sampler
+//       Access: Protected
+//  Description: Updates OpenGL with the current information for this
+//               sampler, and makes it the current sampler available
+//               for rendering.  Use NULL to unbind the sampler.
+//
+//               If the GSG doesn't support sampler objects, the
+//               sampler settings are applied to the given texture
+//               context instead.
+////////////////////////////////////////////////////////////////////
+bool CLP(GraphicsStateGuardian)::
+apply_sampler(GLuint unit, const SamplerState &sampler, TextureContext *tc) {
+  if (_supports_sampler_objects) {
+    // We support sampler objects.  Prepare the sampler object and
+    // bind it to the indicated texture unit.
+    SamplerContext *sc = sampler.prepare_now(get_prepared_objects(), this);
+    nassertr(sc != (SamplerContext *)NULL, false);
+    CLP(SamplerContext) *gsc = DCAST(CLP(SamplerContext), sc);
+
+    gsc->enqueue_lru(&_prepared_objects->_sampler_object_lru);
+
+    _glBindSampler(unit, gsc->_index);
+
+    if (GLCAT.is_spam()) {
+      GLCAT.spam()
+        << "bind " << unit << " " << sampler << "\n";
+    }
+
+  } else {
+    CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
+
+    // We don't support sampler objects.  We'll have to bind the
+    // texture and change the texture parameters if they don't match.
+    if (gtc->_active_sampler != sampler) {
+      _glActiveTexture(GL_TEXTURE0 + unit);
+      apply_texture(tc);
+      specify_texture(gtc, sampler);
+    }
+  }
+
+  report_my_gl_errors();
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::upload_texture
 //       Access: Protected

+ 26 - 5
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -184,10 +184,18 @@ typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size
 typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
 #endif  // OPENGLES_1
 #ifndef OPENGLES
+typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers);
+typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers);
+typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param);
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param);
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param);
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param);
 typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value);
 typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount);
 typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount);
 typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures);
+typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers);
 typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);
 typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures);
 typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);
@@ -276,6 +284,9 @@ public:
   virtual void release_texture(TextureContext *tc);
   virtual bool extract_texture_data(Texture *tex);
 
+  virtual SamplerContext *prepare_sampler(const SamplerState &sampler);
+  virtual void release_sampler(SamplerContext *sc);
+
   virtual GeomContext *prepare_geom(Geom *geom);
   virtual void release_geom(GeomContext *gc);
 
@@ -443,11 +454,11 @@ protected:
 
   static GLenum get_numeric_type(Geom::NumericType numeric_type);
   GLenum get_texture_target(Texture::TextureType texture_type) const;
-  GLenum get_texture_wrap_mode(Texture::WrapMode wm) const;
-  static Texture::WrapMode get_panda_wrap_mode(GLenum wm);
-  static GLenum get_texture_filter_type(Texture::FilterType ft,
+  GLenum get_texture_wrap_mode(SamplerState::WrapMode wm) const;
+  static SamplerState::WrapMode get_panda_wrap_mode(GLenum wm);
+  static GLenum get_texture_filter_type(SamplerState::FilterType ft,
                                         bool ignore_mipmaps);
-  static Texture::FilterType get_panda_filter_type(GLenum ft);
+  static SamplerState::FilterType get_panda_filter_type(GLenum ft);
   GLenum get_component_type(Texture::ComponentType component_type);
   GLint get_external_image_format(Texture *tex) const;
   GLint get_internal_image_format(Texture *tex) const;
@@ -476,8 +487,9 @@ protected:
 #endif  // NDEBUG
 
   void do_auto_rescale_normal();
-  bool specify_texture(CLP(TextureContext) *gtc);
+  bool specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler);
   bool apply_texture(TextureContext *tc);
+  bool apply_sampler(GLuint unit, const SamplerState &sampler, TextureContext *tc);
   bool upload_texture(CLP(TextureContext) *gtc, bool force);
   bool upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
                             bool uses_mipmaps, int mipmap_bias,
@@ -765,11 +777,19 @@ public:
   PFNGLVERTEXATTRIBLPOINTERPROC _glVertexAttribLPointer;
 #endif  // OPENGLES_1
 #ifndef OPENGLES
+  PFNGLGENSAMPLERSPROC _glGenSamplers;
+  PFNGLDELETESAMPLERSPROC _glDeleteSamplers;
+  PFNGLBINDSAMPLERPROC _glBindSampler;
+  PFNGLSAMPLERPARAMETERIPROC _glSamplerParameteri;
+  PFNGLSAMPLERPARAMETERIVPROC _glSamplerParameteriv;
+  PFNGLSAMPLERPARAMETERFPROC _glSamplerParameterf;
+  PFNGLSAMPLERPARAMETERFVPROC _glSamplerParameterfv;
   PFNGLPROGRAMPARAMETERIPROC _glProgramParameteri;
   PFNGLPATCHPARAMETERIPROC _glPatchParameteri;
   PFNGLDRAWARRAYSINSTANCEDPROC _glDrawArraysInstanced;
   PFNGLDRAWELEMENTSINSTANCEDPROC _glDrawElementsInstanced;
   PFNGLBINDTEXTURESPROC _glBindTextures;
+  PFNGLBINDSAMPLERSPROC _glBindSamplers;
   PFNGLBINDIMAGETEXTUREPROC _glBindImageTexture;
   PFNGLBINDIMAGETEXTURESPROC _glBindImageTextures;
   PFNGLDISPATCHCOMPUTEPROC _glDispatchCompute;
@@ -780,6 +800,7 @@ public:
   PFNGLSCISSORARRAYVPROC _glScissorArrayv;
   PFNGLDEPTHRANGEARRAYVPROC _glDepthRangeArrayv;
   PFNGLGETTEXTUREHANDLEPROC _glGetTextureHandle;
+  PFNGLGETTEXTURESAMPLERHANDLEPROC _glGetTextureSamplerHandle;
   PFNGLMAKETEXTUREHANDLERESIDENTPROC _glMakeTextureHandleResident;
   PFNGLMAKETEXTUREHANDLENONRESIDENTPROC _glMakeTextureHandleNonResident;
   PFNGLUNIFORMHANDLEUI64PROC _glUniformHandleui64;

+ 83 - 0
panda/src/glstuff/glSamplerContext_src.cxx

@@ -0,0 +1,83 @@
+// Filename: glSamplerContext_src.cxx
+// Created by:  rdb (11Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pnotify.h"
+
+TypeHandle CLP(SamplerContext)::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(SamplerContext)::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CLP(SamplerContext)::
+CLP(SamplerContext)(CLP(GraphicsStateGuardian) *glgsg,
+  const SamplerState &sampler) :
+  SamplerContext(sampler)
+{
+  _glgsg = glgsg;
+  _glgsg->_glGenSamplers(1, &_index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(SamplerContext)::Denstructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+CLP(SamplerContext)::
+~CLP(SamplerContext)() {
+  if (_index != 0) {
+    _glgsg->_glDeleteSamplers(1, &_index);
+    _index = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLSamplerContext::evict_lru
+//       Access: Public, Virtual
+//  Description: Evicts the page from the LRU.  Called internally when
+//               the LRU determines that it is full.  May also be
+//               called externally when necessary to explicitly evict
+//               the page.
+//
+//               It is legal for this method to either evict the page
+//               as requested, do nothing (in which case the eviction
+//               will be requested again at the next epoch), or
+//               requeue itself on the tail of the queue (in which
+//               case the eviction will be requested again much
+//               later).
+////////////////////////////////////////////////////////////////////
+void CLP(SamplerContext)::
+evict_lru() {
+  dequeue_lru();
+
+  reset_data();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLSamplerContext::reset_data
+//       Access: Public
+//  Description: Resets the texture object to a new one so a new GL
+//               texture object can be uploaded.
+////////////////////////////////////////////////////////////////////
+void CLP(SamplerContext)::
+reset_data() {
+  // Free the sampler resource.
+  _glgsg->_glDeleteSamplers(1, &_index);
+  _index = 0;
+
+  // We still need a valid index number, though, in case we want to
+  // re-load the sampler later.
+  //glGenSamplers(1, &_index);
+}

+ 58 - 0
panda/src/glstuff/glSamplerContext_src.h

@@ -0,0 +1,58 @@
+// Filename: glSamplerContext_src.h
+// Created by:  rdb (11Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+#include "samplerContext.h"
+#include "deletedChain.h"
+
+class CLP(GraphicsStateGuardian);
+
+////////////////////////////////////////////////////////////////////
+//       Class : GLSamplerContext
+// Description : This class represents a sampler object, which
+//               contains a set of sampler parameters used when
+//               sampling a texture.
+////////////////////////////////////////////////////////////////////
+class EXPCL_GL CLP(SamplerContext) : public SamplerContext {
+public:
+  INLINE CLP(SamplerContext)(CLP(GraphicsStateGuardian) *glgsg,
+                             const SamplerState &sampler);
+  ALLOC_DELETED_CHAIN(CLP(SamplerContext));
+
+  virtual ~CLP(SamplerContext)();
+  virtual void evict_lru();
+  void reset_data();
+
+  // This is the GL "name" of the sampler object.
+  GLuint _index;
+
+  CLP(GraphicsStateGuardian) *_glgsg;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    SamplerContext::init_type();
+    register_type(_type_handle, CLASSPREFIX_QUOTED "SamplerContext",
+                  SamplerContext::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;
+};

+ 5 - 0
panda/src/glstuff/glShaderContext_src.cxx

@@ -1276,9 +1276,12 @@ update_shader_texture_bindings(ShaderContext *prev) {
 
     Texture *tex = NULL;
     int view = _glgsg->get_current_tex_view_offset();
+    SamplerState sampler;
+
     if (id != NULL) {
       const ShaderInput *input = _glgsg->_target_shader->get_shader_input(id);
       tex = input->get_texture();
+      sampler = input->get_sampler();
 
     } else {
       if (texunit >= texattrib->get_num_on_stages()) {
@@ -1286,6 +1289,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
       }
       TextureStage *stage = texattrib->get_on_stage(texunit);
       tex = texattrib->get_on_texture(stage);
+      sampler = texattrib->get_on_sampler(stage);
       view += stage->get_tex_view_offset();
     }
 
@@ -1349,6 +1353,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
       continue;
     }
     _glgsg->apply_texture(gtc);
+    _glgsg->apply_sampler(i, sampler, gtc);
   }
 
 #ifndef OPENGLES

+ 3 - 0
panda/src/glstuff/glTextureContext_src.h

@@ -15,8 +15,10 @@
 #include "pandabase.h"
 #include "textureContext.h"
 #include "deletedChain.h"
+#include "samplerState.h"
 
 class CLP(GraphicsStateGuardian);
+class CLP(SamplerContext);
 
 ////////////////////////////////////////////////////////////////////
 //       Class : GLTextureContext
@@ -62,6 +64,7 @@ public:
   GLsizei _height;
   GLsizei _depth;
   GLenum _target;
+  SamplerState _active_sampler;
 
   CLP(GraphicsStateGuardian) *_glgsg;
 

+ 7 - 0
panda/src/glstuff/glmisc_src.cxx

@@ -259,6 +259,12 @@ ConfigVariableBool gl_support_primitive_restart_index
             "segment primitives.  Set to false if you suspect a bug "
             "in the driver implementation."));
 
+ConfigVariableBool gl_support_sampler_objects
+  ("gl-support-sampler-objects", true,
+   PRC_DESC("Setting this allows Panda to make use of sampler "
+            "objects.  Set to false if you suspect a bug in the "
+            "driver implementation."));
+
 extern ConfigVariableBool gl_parallel_arrays;
 
 void CLP(init_classes)() {
@@ -270,6 +276,7 @@ void CLP(init_classes)() {
   CLP(ShaderContext)::init_type();
 #endif
   CLP(TextureContext)::init_type();
+  CLP(SamplerContext)::init_type();
   CLP(VertexBufferContext)::init_type();
   CLP(GraphicsBuffer)::init_type();
 

+ 1 - 0
panda/src/glstuff/glmisc_src.h

@@ -73,6 +73,7 @@ extern ConfigVariableBool gl_use_bindless_texture;
 extern ConfigVariableBool gl_enable_memory_barriers;
 extern ConfigVariableBool gl_vertex_array_objects;
 extern ConfigVariableBool gl_support_primitive_restart_index;
+extern ConfigVariableBool gl_support_sampler_objects;
 
 extern EXPCL_GL void CLP(init_classes)();
 

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

@@ -19,6 +19,7 @@
 
 #include "glmisc_src.cxx"
 #include "glTextureContext_src.cxx"
+#include "glSamplerContext_src.cxx"
 #include "glVertexBufferContext_src.cxx"
 #include "glIndexBufferContext_src.cxx"
 #include "glOcclusionQueryContext_src.cxx"

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

@@ -33,6 +33,7 @@
 
 #include "glmisc_src.h"
 #include "glTextureContext_src.h"
+#include "glSamplerContext_src.h"
 #include "glVertexBufferContext_src.h"
 #include "glIndexBufferContext_src.h"
 #include "glOcclusionQueryContext_src.h"

+ 20 - 1
panda/src/gobj/config_gobj.cxx

@@ -31,12 +31,13 @@
 #include "geomVertexArrayFormat.h"
 #include "geomVertexData.h"
 #include "geomVertexFormat.h"
+#include "lens.h"
 #include "material.h"
 #include "occlusionQueryContext.h"
 #include "orthographicLens.h"
 #include "matrixLens.h"
+#include "paramTexture.h"
 #include "perspectiveLens.h"
-#include "lens.h"
 #include "queryContext.h"
 #include "sliderTable.h"
 #include "texture.h"
@@ -45,6 +46,8 @@
 #include "textureStage.h"
 #include "textureContext.h"
 #include "timerQueryContext.h"
+#include "samplerContext.h"
+#include "samplerState.h"
 #include "shader.h"
 #include "shaderContext.h"
 #include "transformBlend.h"
@@ -480,6 +483,16 @@ ConfigVariableInt graphics_memory_limit
           "Set this to -1 to have no limit other than the normal "
           "hardware-imposed limit."));
 
+ConfigVariableInt sampler_object_limit
+("sampler-object-limit", 2048,
+ PRC_DESC("This is a default limit that is imposed on each GSG at "
+          "GSG creation time.  It limits the total amount of sampler "
+          "objects that will be k.ept by the GSG, regardless of whether "
+          "the hardware claims to provide more sampler objects than this. "
+          "Direct3D 10-capable hardware supports at least 4096 distinct "
+          "sampler objects, but we provide a slightly more conservative "
+          "limit by default."));
+
 ConfigVariableDouble adaptive_lru_weight
 ("adaptive-lru-weight", 0.2,
  PRC_DESC("Specifies the weight factor used to compute the AdaptiveLru's "
@@ -559,8 +572,12 @@ ConfigureFn(config_gobj) {
   MatrixLens::init_type();
   OcclusionQueryContext::init_type();
   OrthographicLens::init_type();
+  ParamTextureImage::init_type();
+  ParamTextureSampler::init_type();
   PerspectiveLens::init_type();
   QueryContext::init_type();
+  SamplerContext::init_type();
+  SamplerState::init_type();
   ShaderContext::init_type();
   Shader::init_type();
   SliderTable::init_type();
@@ -600,6 +617,8 @@ ConfigureFn(config_gobj) {
   Material::register_with_read_factory();
   MatrixLens::register_with_read_factory();
   OrthographicLens::register_with_read_factory();
+  ParamTextureImage::register_with_read_factory();
+  ParamTextureSampler::register_with_read_factory();
   PerspectiveLens::register_with_read_factory();
   Shader::register_with_read_factory();
   SliderTable::register_with_read_factory();

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

@@ -96,6 +96,7 @@ extern EXPCL_PANDA_GOBJ ConfigVariableString vertex_save_file_prefix;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt vertex_data_small_size;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt vertex_data_page_threads;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt graphics_memory_limit;
+extern EXPCL_PANDA_GOBJ ConfigVariableInt sampler_object_limit;
 extern EXPCL_PANDA_GOBJ ConfigVariableDouble adaptive_lru_weight;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt adaptive_lru_max_updates_per_frame;
 extern EXPCL_PANDA_GOBJ ConfigVariableDouble async_load_delay;

+ 2 - 0
panda/src/gobj/p3gobj_composite1.cxx

@@ -33,3 +33,5 @@
 #include "material.cxx"
 #include "materialPool.cxx"
 #include "matrixLens.cxx"
+#include "occlusionQueryContext.cxx"
+#include "orthographicLens.cxx"

+ 3 - 2
panda/src/gobj/p3gobj_composite2.cxx

@@ -1,8 +1,9 @@
-#include "occlusionQueryContext.cxx"
-#include "orthographicLens.cxx"
+#include "paramTexture.cxx"
 #include "perspectiveLens.cxx"
 #include "preparedGraphicsObjects.cxx"
 #include "queryContext.cxx"
+#include "samplerContext.cxx"
+#include "samplerState.cxx"
 #include "savedContext.cxx"
 #include "shaderContext.cxx"
 #include "shader.cxx"

+ 158 - 0
panda/src/gobj/paramTexture.I

@@ -0,0 +1,158 @@
+// Filename: paramTexture.I
+// Created by:  rdb (11Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::Constructor
+//       Access: Published
+//  Description: Creates a new ParamTextureSampler storing the given
+//               texture and sampler objects.
+////////////////////////////////////////////////////////////////////
+INLINE ParamTextureSampler::
+ParamTextureSampler(Texture *tex, const SamplerState &sampler) :
+  _texture(tex),
+  _sampler(sampler)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::get_value_type
+//       Access: Published, Virtual
+//  Description: Returns Texture::get_class_type(), even though it
+//               technically stores more than just a Texture.
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle ParamTextureSampler::
+get_value_type() const {
+  return Texture::get_class_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::get_texture
+//       Access: Published
+//  Description: Retrieves the texture stored in the parameter.
+////////////////////////////////////////////////////////////////////
+INLINE Texture *ParamTextureSampler::
+get_texture() const {
+  return _texture;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::get_sampler
+//       Access: Published
+//  Description: Retrieves the sampler state stored in the parameter.
+////////////////////////////////////////////////////////////////////
+INLINE const SamplerState &ParamTextureSampler::
+get_sampler() const {
+  return _sampler;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::Constructor
+//       Access: Published
+//  Description: Creates a new ParamTextureImage storing the given
+//               texture and image binding parameters.
+////////////////////////////////////////////////////////////////////
+INLINE ParamTextureImage::
+ParamTextureImage(Texture *tex, bool read, bool write, int z, int n) :
+  _texture(tex),
+  _access(0),
+  _bind_level(min(n, 127)),
+  _bind_layer(z)
+{
+  if (read) {
+    _access |= A_read;
+  }
+  if (write) {
+    _access |= A_write;
+  }
+  if (z < 0) {
+    _bind_layer = 0;
+    _access |= A_layered;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::get_value_type
+//       Access: Published, Virtual
+//  Description: Returns Texture::get_class_type(), even though it
+//               technically stores more than just a Texture.
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle ParamTextureImage::
+get_value_type() const {
+  return Texture::get_class_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::get_texture
+//       Access: Published
+//  Description: Retrieves the texture stored in the parameter.
+////////////////////////////////////////////////////////////////////
+INLINE Texture *ParamTextureImage::
+get_texture() const {
+  return _texture;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::has_read_access
+//       Access: Published
+//  Description: Returns true if this image should be bound with
+//               read access enabled.
+////////////////////////////////////////////////////////////////////
+INLINE bool ParamTextureImage::
+has_read_access() const {
+  return (_access & A_read) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::has_write_access
+//       Access: Published
+//  Description: Returns true if this image should be bound with
+//               write access enabled.
+////////////////////////////////////////////////////////////////////
+INLINE bool ParamTextureImage::
+has_write_access() const {
+  return (_access & A_write) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::get_bind_layered
+//       Access: Published
+//  Description: Returns true if all layers of this image should be
+//               bound simultaneously.
+////////////////////////////////////////////////////////////////////
+INLINE bool ParamTextureImage::
+get_bind_layered() const {
+  return (_access & A_layered) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::get_bind_level
+//       Access: Published
+//  Description: Returns the image level that should be bound.
+////////////////////////////////////////////////////////////////////
+INLINE int ParamTextureImage::
+get_bind_level() const {
+  return _bind_level;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::get_bind_layer
+//       Access: Published
+//  Description: Returns the image layer that should be bound.  This
+//               is undefined if get_bind_layered() returns false.
+////////////////////////////////////////////////////////////////////
+INLINE int ParamTextureImage::
+get_bind_layer() const {
+  return _bind_layer;
+}

+ 220 - 0
panda/src/gobj/paramTexture.cxx

@@ -0,0 +1,220 @@
+// Filename: paramTexture.cxx
+// Created by:  rdb (11Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "paramTexture.h"
+#include "dcast.h"
+
+TypeHandle ParamTextureSampler::_type_handle;
+TypeHandle ParamTextureImage::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::output
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void ParamTextureSampler::
+output(ostream &out) const {
+  out << "texture ";
+
+  if (_texture != (Texture *)NULL) {
+    out << _texture->get_name();
+  } else {
+    out << "(empty)";
+  }
+  out << ", ";
+
+  _sampler.output(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               ParamValue.
+////////////////////////////////////////////////////////////////////
+void ParamTextureSampler::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void ParamTextureSampler::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  ParamValueBase::write_datagram(manager, dg);
+  manager->write_pointer(dg, _texture);
+  _sampler.write_datagram(dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int ParamTextureSampler::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = ParamValueBase::complete_pointers(p_list, manager);
+  _texture = DCAST(Texture, p_list[pi++]);
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type ParamValue is encountered
+//               in the Bam file.  It should create the ParamValue
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *ParamTextureSampler::
+make_from_bam(const FactoryParams &params) {
+  ParamTextureSampler *param = new ParamTextureSampler;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureSampler::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new ParamValue.
+////////////////////////////////////////////////////////////////////
+void ParamTextureSampler::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  ParamValueBase::fillin(scan, manager);
+  manager->read_pointer(scan);
+  _sampler.read_datagram(scan, manager);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::output
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void ParamTextureImage::
+output(ostream &out) const {
+  out << "texture ";
+
+  if (_texture != (Texture *)NULL) {
+    out << _texture->get_name();
+  } else {
+    out << "(empty)";
+  }
+
+  if (_access & A_read) {
+    if (_access & A_write) {
+      out << ", read-write";
+    } else {
+      out << ", read-only";
+    }
+  } else if (_access & A_write) {
+    out << ", write-only";
+  }
+
+  if (_access & A_layered) {
+    out << ", all layers";
+  } else {
+    out << ", layer " << _bind_layer;
+  }
+
+  out << ", level " << _bind_level;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               ParamValue.
+////////////////////////////////////////////////////////////////////
+void ParamTextureImage::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void ParamTextureImage::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  ParamValueBase::write_datagram(manager, dg);
+  manager->write_pointer(dg, _texture);
+  dg.add_uint8(_access);
+  dg.add_int8(_bind_level);
+  dg.add_int32(_bind_layer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int ParamTextureImage::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = ParamValueBase::complete_pointers(p_list, manager);
+  _texture = DCAST(Texture, p_list[pi++]);
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type ParamValue is encountered
+//               in the Bam file.  It should create the ParamValue
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *ParamTextureImage::
+make_from_bam(const FactoryParams &params) {
+  ParamTextureImage *param = new ParamTextureImage;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ParamTextureImage::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new ParamValue.
+////////////////////////////////////////////////////////////////////
+void ParamTextureImage::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  ParamValueBase::fillin(scan, manager);
+  manager->read_pointer(scan);
+  _access = scan.get_uint8();
+  _bind_level = scan.get_int8();
+  _bind_layer = scan.get_int32();
+}

+ 143 - 0
panda/src/gobj/paramTexture.h

@@ -0,0 +1,143 @@
+// Filename: paramTexture.h
+// Created by:  rdb (11Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PARAMTEXTURE_H
+#define PARAMTEXTURE_H
+
+#include "pandabase.h"
+#include "paramValue.h"
+#include "samplerState.h"
+#include "texture.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ParamTextureSampler
+// Description : A class object for storing a pointer to a Texture
+//               along with a sampler state that indicates how to
+//               to sample the given texture.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GOBJ ParamTextureSampler : public ParamValueBase {
+protected:
+  INLINE ParamTextureSampler() {};
+
+PUBLISHED:
+  INLINE ParamTextureSampler(Texture *tex, const SamplerState &sampler);
+
+  INLINE virtual TypeHandle get_value_type() const;
+  INLINE Texture *get_texture() const;
+  INLINE const SamplerState &get_sampler() const;
+
+  virtual void output(ostream &out) const;
+
+private:
+  PT(Texture) _texture;
+  SamplerState _sampler;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist,
+                                BamReader *manager);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ParamValueBase::init_type();
+    register_type(_type_handle, "ParamTextureSampler",
+                  ParamValueBase::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : ParamTextureImage
+// Description : A class object for storing a pointer to a Texture
+//               along with a set of properties that indicates which
+//               image to bind to a shader input.
+//
+//               This class is useful for binding texture images
+//               to a shader, which is a fairly esoteric feature.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GOBJ ParamTextureImage : public ParamValueBase {
+protected:
+  INLINE ParamTextureImage() {};
+
+  enum AccessFlags {
+    A_read    = 0x01,
+    A_write   = 0x02,
+    A_layered = 0x04,
+  };
+
+PUBLISHED:
+  INLINE ParamTextureImage(Texture *tex, bool read, bool write, int z=-1, int n=0);
+
+  INLINE virtual TypeHandle get_value_type() const;
+
+  INLINE Texture *get_texture() const;
+  INLINE bool has_read_access() const;
+  INLINE bool has_write_access() const;
+  INLINE bool get_bind_layered() const;
+  INLINE int get_bind_level() const;
+  INLINE int get_bind_layer() const;
+
+  virtual void output(ostream &out) const;
+
+private:
+  PT(Texture) _texture;
+  int _access : 4;
+  int _bind_level : 8;
+  int _bind_layer : 20;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist,
+                                BamReader *manager);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ParamValueBase::init_type();
+    register_type(_type_handle, "ParamTextureImage",
+                  ParamValueBase::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "paramTexture.I"
+
+#endif

+ 8 - 5
panda/src/gobj/preparedGraphicsObjects.I

@@ -45,6 +45,7 @@ get_graphics_memory_limit() const {
 INLINE void PreparedGraphicsObjects::
 release_all() {
   release_all_textures();
+  release_all_samplers();
   release_all_geoms();
   release_all_shaders();
   release_all_vertex_buffers();
@@ -63,7 +64,8 @@ release_all() {
 ////////////////////////////////////////////////////////////////////
 INLINE int PreparedGraphicsObjects::
 get_num_queued() const {
-  return (get_num_queued_textures() + 
+  return (get_num_queued_textures() +
+          get_num_queued_samplers() +
           get_num_queued_geoms() +
           get_num_queued_shaders() +
           get_num_queued_vertex_buffers() +
@@ -78,7 +80,8 @@ get_num_queued() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int PreparedGraphicsObjects::
 get_num_prepared() const {
-  return (get_num_prepared_textures() + 
+  return (get_num_prepared_textures() +
+          get_num_prepared_samplers() +
           get_num_prepared_geoms() +
           get_num_prepared_shaders() +
           get_num_prepared_vertex_buffers() +
@@ -88,7 +91,7 @@ get_num_prepared() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: PreparedGraphicsObjects::BufferCacheKey::operator <
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool PreparedGraphicsObjects::BufferCacheKey::
 operator < (const PreparedGraphicsObjects::BufferCacheKey &other) const {
@@ -101,7 +104,7 @@ operator < (const PreparedGraphicsObjects::BufferCacheKey &other) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: PreparedGraphicsObjects::BufferCacheKey::operator ==
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool PreparedGraphicsObjects::BufferCacheKey::
 operator == (const PreparedGraphicsObjects::BufferCacheKey &other) const {
@@ -112,7 +115,7 @@ operator == (const PreparedGraphicsObjects::BufferCacheKey &other) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: PreparedGraphicsObjects::BufferCacheKey::operator !=
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool PreparedGraphicsObjects::BufferCacheKey::
 operator != (const PreparedGraphicsObjects::BufferCacheKey &other) const {

+ 232 - 13
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -20,6 +20,7 @@
 #include "geom.h"
 #include "geomVertexArrayData.h"
 #include "geomPrimitive.h"
+#include "samplerContext.h"
 #include "shader.h"
 #include "reMutexHolder.h"
 #include "geomContext.h"
@@ -32,10 +33,10 @@ int PreparedGraphicsObjects::_name_index = 0;
 ////////////////////////////////////////////////////////////////////
 //     Function: PreparedGraphicsObjects::Constructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 PreparedGraphicsObjects::
-PreparedGraphicsObjects() : 
+PreparedGraphicsObjects() :
   _lock("PreparedGraphicsObjects::_lock"),
   _name(init_name()),
   _vertex_buffer_cache_size(0),
@@ -43,7 +44,8 @@ PreparedGraphicsObjects() :
   _texture_residency(_name, "texture"),
   _vbuffer_residency(_name, "vbuffer"),
   _ibuffer_residency(_name, "ibuffer"),
-  _graphics_memory_lru("graphics_memory_lru", graphics_memory_limit)
+  _graphics_memory_lru("graphics_memory_lru", graphics_memory_limit),
+  _sampler_object_lru("sampler_object_lru", sampler_object_limit)
 {
   // GLGSG will turn this flag on.  This is a temporary hack to
   // disable this feature for DX8/DX9 for now, until we work out the
@@ -54,7 +56,7 @@ PreparedGraphicsObjects() :
 ////////////////////////////////////////////////////////////////////
 //     Function: PreparedGraphicsObjects::Destructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 PreparedGraphicsObjects::
 ~PreparedGraphicsObjects() {
@@ -75,6 +77,9 @@ PreparedGraphicsObjects::
   // Is this a leak?  Should we delete these TextureContexts?
   _released_textures.clear();
 
+  release_all_samplers();
+  _released_samplers.clear();
+
   release_all_geoms();
   _released_geoms.clear();
 
@@ -121,7 +126,7 @@ void PreparedGraphicsObjects::
 set_graphics_memory_limit(size_t limit) {
   if (limit != _graphics_memory_lru.get_max_size()) {
     _graphics_memory_lru.set_max_size(limit);
-  
+
     // We throw an event here so global objects (particularly the
     // TexMemWatcher) can automatically respond to this change.
     throw_event("graphics_memory_limit_changed");
@@ -358,6 +363,198 @@ prepare_texture_now(Texture *tex, int view, GraphicsStateGuardianBase *gsg) {
   return tc;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::enqueue_sampler
+//       Access: Public
+//  Description: Indicates that a sampler would like to be put on the
+//               list to be prepared when the GSG is next ready to
+//               do this (presumably at the next frame).
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+enqueue_sampler(const SamplerState &sampler) {
+  ReMutexHolder holder(_lock);
+
+  _enqueued_samplers.insert(sampler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::is_sampler_queued
+//       Access: Public
+//  Description: Returns true if the sampler has been queued on this
+//               GSG, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool PreparedGraphicsObjects::
+is_sampler_queued(const SamplerState &sampler) const {
+  ReMutexHolder holder(_lock);
+
+  EnqueuedSamplers::const_iterator qi = _enqueued_samplers.find(sampler);
+  return (qi != _enqueued_samplers.end());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::dequeue_sampler
+//       Access: Public
+//  Description: Removes a sampler from the queued list of samplers to
+//               be prepared.  Normally it is not necessary to call
+//               this, unless you change your mind about preparing it
+//               at the last minute, since the sampler will
+//               automatically be dequeued and prepared at the next
+//               frame.
+//
+//               The return value is true if the sampler is
+//               successfully dequeued, false if it had not been
+//               queued.
+////////////////////////////////////////////////////////////////////
+bool PreparedGraphicsObjects::
+dequeue_sampler(const SamplerState &sampler) {
+  ReMutexHolder holder(_lock);
+
+  EnqueuedSamplers::iterator qi = _enqueued_samplers.find(sampler);
+  if (qi != _enqueued_samplers.end()) {
+    _enqueued_samplers.erase(qi);
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::is_sampler_prepared
+//       Access: Public
+//  Description: Returns true if the sampler has been prepared on
+//               this GSG, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool PreparedGraphicsObjects::
+is_sampler_prepared(const SamplerState &sampler) const {
+  ReMutexHolder holder(_lock);
+
+  PreparedSamplers::const_iterator it = _prepared_samplers.find(sampler);
+  return (it != _prepared_samplers.end());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::release_sampler
+//       Access: Public
+//  Description: Indicates that a sampler context, created by a
+//               previous call to prepare_sampler(), is no longer
+//               needed.  The driver resources will not be freed until
+//               some GSG calls update(), indicating it is at a
+//               stage where it is ready to release samplers.
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+release_sampler(SamplerContext *sc) {
+  ReMutexHolder holder(_lock);
+
+  _released_samplers.insert(sc);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::release_sampler
+//       Access: Public
+//  Description: Releases a sampler if it has already been prepared,
+//               or removes it from the preparation queue.
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+release_sampler(const SamplerState &sampler) {
+  ReMutexHolder holder(_lock);
+
+  PreparedSamplers::iterator it = _prepared_samplers.find(sampler);
+  if (it != _prepared_samplers.end()) {
+    _released_samplers.insert(it->second);
+    _prepared_samplers.erase(it);
+  }
+
+  _enqueued_samplers.erase(sampler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::release_all_samplers
+//       Access: Public
+//  Description: Releases all samplers at once.  This will force them
+//               to be reloaded for all GSG's that share this object.
+//               Returns the number of samplers released.
+////////////////////////////////////////////////////////////////////
+int PreparedGraphicsObjects::
+release_all_samplers() {
+  ReMutexHolder holder(_lock);
+
+  int num_samplers = (int)_prepared_samplers.size() + (int)_enqueued_samplers.size();
+
+  PreparedSamplers::iterator sci;
+  for (sci = _prepared_samplers.begin();
+       sci != _prepared_samplers.end();
+       ++sci) {
+    _released_samplers.insert(sci->second);
+  }
+
+  _prepared_samplers.clear();
+  _enqueued_samplers.clear();
+
+  return num_samplers;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::get_num_queued_samplers
+//       Access: Public
+//  Description: Returns the number of samplers that have been
+//               enqueued to be prepared on this GSG.
+////////////////////////////////////////////////////////////////////
+int PreparedGraphicsObjects::
+get_num_queued_samplers() const {
+  return _enqueued_samplers.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::get_num_prepared_samplers
+//       Access: Public
+//  Description: Returns the number of samplers that have already been
+//               prepared on this GSG.
+////////////////////////////////////////////////////////////////////
+int PreparedGraphicsObjects::
+get_num_prepared_samplers() const {
+  return _prepared_samplers.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::prepare_sampler_now
+//       Access: Public
+//  Description: Immediately creates a new SamplerContext for the
+//               indicated sampler and returns it.  This assumes that
+//               the GraphicsStateGuardian is the currently active
+//               rendering context and that it is ready to accept new
+//               samplers.  If this is not necessarily the case, you
+//               should use enqueue_sampler() instead.
+//
+//               Normally, this function is not called directly.
+//               Call Sampler::prepare_now() instead.
+//
+//               The SamplerContext contains all of the pertinent
+//               information needed by the GSG to keep track of this
+//               one particular sampler, and will exist as long as the
+//               sampler is ready to be rendered.
+//
+//               When either the Sampler or the
+//               PreparedGraphicsObjects object destructs, the
+//               SamplerContext will be deleted.
+////////////////////////////////////////////////////////////////////
+SamplerContext *PreparedGraphicsObjects::
+prepare_sampler_now(const SamplerState &sampler, GraphicsStateGuardianBase *gsg) {
+  ReMutexHolder holder(_lock);
+
+  PreparedSamplers::const_iterator it = _prepared_samplers.find(sampler);
+  if (it != _prepared_samplers.end()) {
+    return it->second;
+  }
+
+  // Ask the GSG to create a brand new SamplerContext.
+  SamplerContext *sc = gsg->prepare_sampler(sampler);
+
+  if (sc != (SamplerContext *)NULL) {
+    _prepared_samplers[sampler] = sc;
+  }
+
+  return sc;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PreparedGraphicsObjects::enqueue_geom
 //       Access: Public
@@ -870,8 +1067,8 @@ release_all_vertex_buffers() {
 
   // Also clear the cache of recently-unprepared vertex buffers.
   BufferCache::iterator bci;
-  for (bci = _vertex_buffer_cache.begin(); 
-       bci != _vertex_buffer_cache.end(); 
+  for (bci = _vertex_buffer_cache.begin();
+       bci != _vertex_buffer_cache.end();
        ++bci) {
     BufferList &buffer_list = (*bci).second;
     nassertr(!buffer_list.empty(), num_vertex_buffers);
@@ -884,7 +1081,7 @@ release_all_vertex_buffers() {
   _vertex_buffer_cache.clear();
   _vertex_buffer_cache_lru.clear();
   _vertex_buffer_cache_size = 0;
-  
+
   return num_vertex_buffers;
 }
 
@@ -1099,8 +1296,8 @@ release_all_index_buffers() {
 
   // Also clear the cache of recently-unprepared index buffers.
   BufferCache::iterator bci;
-  for (bci = _index_buffer_cache.begin(); 
-       bci != _index_buffer_cache.end(); 
+  for (bci = _index_buffer_cache.begin();
+       bci != _index_buffer_cache.end();
        ++bci) {
     BufferList &buffer_list = (*bci).second;
     nassertr(!buffer_list.empty(), num_index_buffers);
@@ -1218,9 +1415,21 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
       TextureContext *tc = (*tci);
       gsg->release_texture(tc);
     }
+
+    _released_textures.clear();
   }
 
-  _released_textures.clear();
+  if (!_released_samplers.empty()) {
+    ReleasedSamplers::iterator sci;
+    for (sci = _released_samplers.begin();
+         sci != _released_samplers.end();
+         ++sci) {
+      SamplerContext *sc = (*sci);
+      gsg->release_sampler(sc);
+    }
+
+    _released_samplers.clear();
+  }
 
   Geoms::iterator gci;
   for (gci = _released_geoms.begin();
@@ -1284,6 +1493,16 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
 
   _enqueued_textures.clear();
 
+  EnqueuedSamplers::iterator qsmi;
+  for (qsmi = _enqueued_samplers.begin();
+       qsmi != _enqueued_samplers.end();
+       ++qsmi) {
+    const SamplerState &sampler = (*qsmi);
+    sampler.prepare_now(this, gsg);
+  }
+
+  _enqueued_samplers.clear();
+
   EnqueuedGeoms::iterator qgi;
   for (qgi = _enqueued_geoms.begin();
        qgi != _enqueued_geoms.end();
@@ -1371,7 +1590,7 @@ cache_unprepared_buffer(BufferContext *buffer, size_t data_size_bytes,
                         GeomEnums::UsageHint usage_hint,
                         PreparedGraphicsObjects::BufferCache &buffer_cache,
                         PreparedGraphicsObjects::BufferCacheLRU &buffer_cache_lru,
-                        size_t &buffer_cache_size, 
+                        size_t &buffer_cache_size,
                         int released_buffer_cache_size,
                         PreparedGraphicsObjects::Buffers &released_buffers) {
   BufferCacheKey key;
@@ -1395,7 +1614,7 @@ cache_unprepared_buffer(BufferContext *buffer, size_t data_size_bytes,
     nassertv(!buffer_cache_lru.empty());
     const BufferCacheKey &release_key = *buffer_cache_lru.rbegin();
     BufferList &buffer_list = buffer_cache[release_key];
-    while (!buffer_list.empty() && 
+    while (!buffer_list.empty() &&
            (int)buffer_cache_size > released_buffer_cache_size) {
       BufferContext *released_buffer = buffer_list.back();
       buffer_list.pop_back();

+ 31 - 6
panda/src/gobj/preparedGraphicsObjects.h

@@ -18,6 +18,7 @@
 #include "pandabase.h"
 #include "referenceCount.h"
 #include "texture.h"
+#include "samplerState.h"
 #include "geom.h"
 #include "geomVertexArrayData.h"
 #include "geomPrimitive.h"
@@ -30,6 +31,7 @@
 #include "adaptiveLru.h"
 
 class TextureContext;
+class SamplerContext;
 class GeomContext;
 class ShaderContext;
 class VertexBufferContext;
@@ -82,7 +84,20 @@ PUBLISHED:
   int get_num_queued_textures() const;
   int get_num_prepared_textures() const;
 
-  TextureContext *prepare_texture_now(Texture *tex, int view, 
+  TextureContext *prepare_texture_now(Texture *tex, int view,
+                                      GraphicsStateGuardianBase *gsg);
+
+  void enqueue_sampler(const SamplerState &sampler);
+  bool is_sampler_queued(const SamplerState &sampler) const;
+  bool dequeue_sampler(const SamplerState &sampler);
+  bool is_sampler_prepared(const SamplerState &sampler) const;
+  void release_sampler(SamplerContext *sc);
+  void release_sampler(const SamplerState &sampler);
+  int release_all_samplers();
+  int get_num_queued_samplers() const;
+  int get_num_prepared_samplers() const;
+
+  SamplerContext *prepare_sampler_now(const SamplerState &sampler,
                                       GraphicsStateGuardianBase *gsg);
 
   void enqueue_geom(Geom *geom);
@@ -152,6 +167,12 @@ private:
   typedef phash_set< PT(GeomVertexArrayData) > EnqueuedVertexBuffers;
   typedef phash_set< PT(GeomPrimitive) > EnqueuedIndexBuffers;
 
+  // Sampler states are stored a little bit differently, as they are
+  // mapped by value and can't store the list of prepared samplers.
+  typedef pmap<SamplerState, SamplerContext *> PreparedSamplers;
+  typedef pset<SamplerContext *, pointer_hash> ReleasedSamplers;
+  typedef pset<SamplerState> EnqueuedSamplers;
+
   class BufferCacheKey {
   public:
     INLINE bool operator < (const BufferCacheKey &other) const;
@@ -171,7 +192,7 @@ private:
                                size_t &buffer_cache_size,
                                int released_buffer_cache_size,
                                Buffers &released_buffers);
-  BufferContext *get_cached_buffer(size_t data_size_bytes, 
+  BufferContext *get_cached_buffer(size_t data_size_bytes,
                                    GeomEnums::UsageHint usage_hint,
                                    BufferCache &buffer_cache,
                                    BufferCacheLRU &buffer_cache_lru,
@@ -179,15 +200,18 @@ private:
 
   ReMutex _lock;
   string _name;
-  Textures _prepared_textures, _released_textures;  
+  Textures _prepared_textures, _released_textures;
   EnqueuedTextures _enqueued_textures;
-  Geoms _prepared_geoms, _released_geoms;  
+  PreparedSamplers _prepared_samplers;
+  ReleasedSamplers _released_samplers;
+  EnqueuedSamplers _enqueued_samplers;
+  Geoms _prepared_geoms, _released_geoms;
   EnqueuedGeoms _enqueued_geoms;
   Shaders _prepared_shaders, _released_shaders;
   EnqueuedShaders _enqueued_shaders;
-  Buffers _prepared_vertex_buffers, _released_vertex_buffers;  
+  Buffers _prepared_vertex_buffers, _released_vertex_buffers;
   EnqueuedVertexBuffers _enqueued_vertex_buffers;
-  Buffers _prepared_index_buffers, _released_index_buffers;  
+  Buffers _prepared_index_buffers, _released_index_buffers;
   EnqueuedIndexBuffers _enqueued_index_buffers;
 
   BufferCache _vertex_buffer_cache;
@@ -204,6 +228,7 @@ public:
   BufferResidencyTracker _ibuffer_residency;
 
   AdaptiveLru _graphics_memory_lru;
+  SimpleLru _sampler_object_lru;
 
 public:
   // This is only public as a temporary hack.  Don't mess with it

+ 25 - 0
panda/src/gobj/samplerContext.I

@@ -0,0 +1,25 @@
+// Filename: samplerContext.I
+// Created by:  rdb (11Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerContext::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SamplerContext::
+SamplerContext(const SamplerState &sampler) :
+  SimpleLruPage(1)
+{
+}

+ 38 - 0
panda/src/gobj/samplerContext.cxx

@@ -0,0 +1,38 @@
+// Filename: samplerContext.cxx
+// Created by:  rdb (11Dec14))
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "samplerContext.h"
+
+TypeHandle SamplerContext::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerContext::output
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void SamplerContext::
+output(ostream &out) const {
+  SavedContext::output(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerContext::write
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void SamplerContext::
+write(ostream &out, int indent_level) const {
+  SavedContext::write(out, indent_level);
+}
+

+ 73 - 0
panda/src/gobj/samplerContext.h

@@ -0,0 +1,73 @@
+// Filename: samplerContext.h
+// Created by:  rdb (11Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef SAMPLERCONTEXT_H
+#define SAMPLERCONTEXT_H
+
+#include "pandabase.h"
+
+#include "adaptiveLru.h"
+#include "samplerState.h"
+#include "savedContext.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : SamplerContext
+// Description : This is a special class object that holds a handle
+//               to the sampler state object given by the graphics
+//               back-end for a particular combination of texture
+//               sampling settings.
+//
+//               Some graphics back-ends (like OpenGL) use mutable
+//               sampler objects, whereas others (Direct3D 10+) use
+//               immutable ones.  In Panda3D, each unique sampler
+//               state has its own SamplerContext, which simplifies
+//               the implementation and makes redundant sampler
+//               objects impossible.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GOBJ SamplerContext : public SavedContext, public SimpleLruPage {
+public:
+  INLINE SamplerContext(const SamplerState &sampler);
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    SavedContext::init_type();
+    register_type(_type_handle, "SamplerContext",
+                  SavedContext::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 PreparedGraphicsObjects;
+};
+
+inline ostream &operator << (ostream &out, const SamplerContext &context) {
+  context.output(out);
+  return out;
+}
+
+#include "samplerContext.I"
+
+#endif
+

+ 338 - 0
panda/src/gobj/samplerState.I

@@ -0,0 +1,338 @@
+// Filename: samplerState.I
+// Created by:  rdb (09Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::Constructor
+//       Access: Published
+//  Description: Creates a new SamplerState initialized to the
+//               default values.
+////////////////////////////////////////////////////////////////////
+INLINE SamplerState::
+SamplerState() :
+  _border_color(0, 0, 0, 1),
+  _wrap_u(WM_repeat),
+  _wrap_v(WM_repeat),
+  _wrap_w(WM_repeat),
+  _minfilter(FT_default),
+  _magfilter(FT_default),
+  _min_lod(-1000),
+  _max_lod(1000),
+  _lod_bias(0),
+  _anisotropic_degree(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_default
+//       Access: Published, Static
+//  Description: Returns a reference to the global default immutable
+//               SamplerState object.
+////////////////////////////////////////////////////////////////////
+INLINE const SamplerState &SamplerState::
+get_default() {
+  return _default;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_wrap_u
+//       Access: Published
+//  Description: This setting determines what happens when the
+//               SamplerState is sampled with a U value outside the range
+//               0.0-1.0.  The default is WM_repeat, which indicates
+//               that the SamplerState should repeat indefinitely.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_wrap_u(SamplerState::WrapMode wrap) {
+  _wrap_u = wrap;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_wrap_v
+//       Access: Published
+//  Description: This setting determines what happens when the
+//               SamplerState is sampled with a V value outside the range
+//               0.0-1.0.  The default is WM_repeat, which indicates
+//               that the SamplerState should repeat indefinitely.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_wrap_v(SamplerState::WrapMode wrap) {
+  _wrap_v = wrap;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_wrap_w
+//       Access: Published
+//  Description: The W wrap direction is only used for 3-d SamplerStates.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_wrap_w(SamplerState::WrapMode wrap) {
+  _wrap_w = wrap;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_minfilter
+//       Access: Published
+//  Description: Sets the filtering method that should be used when
+//               viewing the SamplerState from a distance.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_minfilter(SamplerState::FilterType filter) {
+  _minfilter = filter;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_magfilter
+//       Access: Published
+//  Description: Sets the filtering method that should be used when
+//               viewing the SamplerState up close.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_magfilter(SamplerState::FilterType filter) {
+  _magfilter = filter;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_anisotropic_degree
+//       Access: Published
+//  Description: Specifies the level of anisotropic filtering to apply
+//               to the SamplerState.  Set this 0 to indicate the default
+//               value, which is specified in the
+//               SamplerState-anisotropic-degree config variable.
+//
+//               To explicitly disable anisotropic filtering, set this
+//               value to 1.  To explicitly enable anisotropic
+//               filtering, set it to a value higher than 1; larger
+//               numbers indicate greater degrees of filtering.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_anisotropic_degree(int anisotropic_degree) {
+  _anisotropic_degree = anisotropic_degree;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_border_color
+//       Access: Published
+//  Description: Specifies the solid color of the SamplerState's border.
+//               Some OpenGL implementations use a border for tiling
+//               SamplerStates; in Panda, it is only used for specifying
+//               the clamp color.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_border_color(const LColor &color) {
+  _border_color = color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_min_lod
+//       Access: Published
+//  Description: Sets the minimum level of detail that will be used
+//               when sampling this texture.  This may be a negative
+//               value.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_min_lod(PN_stdfloat min_lod) {
+  _min_lod = min_lod;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_max_lod
+//       Access: Published
+//  Description: Sets the maximum level of detail that will be used
+//               when sampling this texture.  This may exceed the
+//               number of mipmap levels that the texture has.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_max_lod(PN_stdfloat max_lod) {
+  _max_lod = max_lod;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::set_lod_bias
+//       Access: Published
+//  Description: Sets the value that will be added to the level of
+//               detail when sampling the texture.  This may be a
+//               negative value, although some graphics hardware may
+//               not support the use of negative LOD values.
+////////////////////////////////////////////////////////////////////
+INLINE void SamplerState::
+set_lod_bias(PN_stdfloat lod_bias) {
+  _lod_bias = lod_bias;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_wrap_u
+//       Access: Published
+//  Description: Returns the wrap mode of the texture in the U
+//               direction.
+////////////////////////////////////////////////////////////////////
+INLINE SamplerState::WrapMode SamplerState::
+get_wrap_u() const {
+  return _wrap_u;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_wrap_v
+//       Access: Published
+//  Description: Returns the wrap mode of the texture in the V
+//               direction.
+////////////////////////////////////////////////////////////////////
+INLINE SamplerState::WrapMode SamplerState::
+get_wrap_v() const {
+  return _wrap_v;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_wrap_w
+//       Access: Published
+//  Description: Returns the wrap mode of the texture in the W
+//               direction.  This is the depth direction of 3-d
+//               textures.
+////////////////////////////////////////////////////////////////////
+INLINE SamplerState::WrapMode SamplerState::
+get_wrap_w() const {
+  return _wrap_w;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_minfilter
+//       Access: Published
+//  Description: Returns the filter mode of the texture for
+//               minification.  If this is one of the mipmap
+//               constants, then the texture requires mipmaps.  This
+//               may return FT_default; see also
+//               get_effective_minfilter().
+////////////////////////////////////////////////////////////////////
+INLINE SamplerState::FilterType SamplerState::
+get_minfilter() const {
+  return _minfilter;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_magfilter
+//       Access: Published
+//  Description: Returns the filter mode of the texture for
+//               magnification.  The mipmap constants are invalid
+//               here.  This may return FT_default; see also
+//               get_effective_minfilter().
+////////////////////////////////////////////////////////////////////
+INLINE SamplerState::FilterType SamplerState::
+get_magfilter() const {
+  return _magfilter;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_anisotropic_degree
+//       Access: Published
+//  Description: Returns the degree of anisotropic filtering that
+//               should be applied to the texture.  This value may
+//               return 0, indicating the default value; see also
+//               get_effective_anisotropic_degree.
+////////////////////////////////////////////////////////////////////
+INLINE int SamplerState::
+get_anisotropic_degree() const {
+  return _anisotropic_degree;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_effective_anisotropic_degree
+//       Access: Published
+//  Description: Returns the degree of anisotropic filtering that
+//               should be applied to the texture.  This value will
+//               normally not return 0, unless there is an error in
+//               the config file.
+////////////////////////////////////////////////////////////////////
+INLINE int SamplerState::
+get_effective_anisotropic_degree() const {
+  if (_anisotropic_degree != 0) {
+    return _anisotropic_degree;
+  }
+  return texture_anisotropic_degree;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_border_color
+//       Access: Published
+//  Description: Returns the solid color of the texture's border.
+//               Some OpenGL implementations use a border for tiling
+//               textures; in Panda, it is only used for specifying
+//               the clamp color.
+////////////////////////////////////////////////////////////////////
+INLINE const LColor &SamplerState::
+get_border_color() const {
+  return _border_color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_min_lod
+//       Access: Published
+//  Description: Returns the minimum level of detail that will be
+//               observed when sampling this texture.
+////////////////////////////////////////////////////////////////////
+INLINE PN_stdfloat SamplerState::
+get_min_lod() const {
+  return _min_lod;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_max_lod
+//       Access: Published
+//  Description: Returns the maximum level of detail that will be
+//               observed when sampling this texture.
+////////////////////////////////////////////////////////////////////
+INLINE PN_stdfloat SamplerState::
+get_max_lod() const {
+  return _max_lod;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_lod_bias
+//       Access: Published
+//  Description: Returns the bias that will be added to the texture
+//               level of detail when sampling this texture.
+////////////////////////////////////////////////////////////////////
+INLINE PN_stdfloat SamplerState::
+get_lod_bias() const {
+  return _lod_bias;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::operator ==
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool SamplerState::
+operator == (const SamplerState &other) const {
+  return compare_to(other) == 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::operator !=
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool SamplerState::
+operator != (const SamplerState &other) const {
+  return compare_to(other) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::operator <
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool SamplerState::
+operator < (const SamplerState &other) const {
+  return compare_to(other) < 0;
+}

+ 401 - 0
panda/src/gobj/samplerState.cxx

@@ -0,0 +1,401 @@
+// Filename: samplerState.cxx
+// Created by:  rdb (09Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "samplerState.h"
+#include "indent.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "samplerContext.h"
+#include "preparedGraphicsObjects.h"
+
+TypeHandle SamplerState::_type_handle;
+SamplerState SamplerState::_default;
+
+ConfigVariableEnum<SamplerState::FilterType> texture_minfilter
+("texture-minfilter", SamplerState::FT_linear,
+ PRC_DESC("This specifies the default minfilter that is applied to a texture "
+          "in the absence of a specific minfilter setting.  Normally this "
+          "is either 'linear' to disable mipmapping by default, or "
+          "'mipmap', to enable trilinear mipmapping by default.  This "
+          "does not apply to depth textures.  Note if this variable is "
+          "changed at runtime, you may need to reload textures explicitly "
+          "in order to change their visible properties."));
+
+ConfigVariableEnum<SamplerState::FilterType> texture_magfilter
+("texture-magfilter", SamplerState::FT_linear,
+ PRC_DESC("This specifies the default magfilter that is applied to a texture "
+          "in the absence of a specific magfilter setting.  Normally this "
+          "is 'linear' (since mipmapping does not apply to magfilters).  This "
+          "does not apply to depth textures.  Note if this variable is "
+          "changed at runtime, you may need to reload textures explicitly "
+          "in order to change their visible properties."));
+
+ConfigVariableInt texture_anisotropic_degree
+("texture-anisotropic-degree", 1,
+ PRC_DESC("This specifies the default anisotropic degree that is applied "
+          "to a texture in the absence of a particular anisotropic degree "
+          "setting (that is, a texture for which the anisotropic degree "
+          "is 0, meaning the default setting).  It should be 1 to disable "
+          "anisotropic filtering, or a higher number to enable it.  "
+          "Note if this variable is "
+          "changed at runtime, you may need to reload textures explicitly "
+          "in order to change their visible properties."));
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_effective_minfilter
+//       Access: Published
+//  Description: Returns the filter mode of the texture for
+//               minification, with special treatment for FT_default.
+//               This will normally not return FT_default, unless
+//               there is an error in the config file.
+////////////////////////////////////////////////////////////////////
+SamplerState::FilterType SamplerState::
+get_effective_minfilter() const {
+  if (_minfilter != FT_default) {
+    return _minfilter;
+  }
+  return texture_minfilter;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::get_effective_magfilter
+//       Access: Published
+//  Description: Returns the filter mode of the texture for
+//               magnification, with special treatment for FT_default.
+//               This will normally not return FT_default, unless
+//               there is an error in the config file.
+////////////////////////////////////////////////////////////////////
+SamplerState::FilterType SamplerState::
+get_effective_magfilter() const {
+  if (_magfilter != FT_default) {
+    return _magfilter;
+  }
+  return texture_magfilter;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::format_filter_type
+//       Access: Published, Static
+//  Description: Returns the indicated FilterType converted to a
+//               string word.
+////////////////////////////////////////////////////////////////////
+string SamplerState::
+format_filter_type(FilterType ft) {
+  switch (ft) {
+  case FT_nearest:
+    return "nearest";
+  case FT_linear:
+    return "linear";
+
+  case FT_nearest_mipmap_nearest:
+    return "nearest_mipmap_nearest";
+  case FT_linear_mipmap_nearest:
+    return "linear_mipmap_nearest";
+  case FT_nearest_mipmap_linear:
+    return "nearest_mipmap_linear";
+  case FT_linear_mipmap_linear:
+    return "linear_mipmap_linear";
+
+  case FT_shadow:
+    return "shadow";
+
+  case FT_default:
+    return "default";
+
+  case FT_invalid:
+    return "invalid";
+  }
+  return "**invalid**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::string_filter_type
+//       Access: Public
+//  Description: Returns the FilterType value associated with the given
+//               string representation, or FT_invalid if the string
+//               does not match any known FilterType value.
+////////////////////////////////////////////////////////////////////
+SamplerState::FilterType SamplerState::
+string_filter_type(const string &string) {
+  if (cmp_nocase_uh(string, "nearest") == 0) {
+    return FT_nearest;
+  } else if (cmp_nocase_uh(string, "linear") == 0) {
+    return FT_linear;
+  } else if (cmp_nocase_uh(string, "nearest_mipmap_nearest") == 0) {
+    return FT_nearest_mipmap_nearest;
+  } else if (cmp_nocase_uh(string, "linear_mipmap_nearest") == 0) {
+    return FT_linear_mipmap_nearest;
+  } else if (cmp_nocase_uh(string, "nearest_mipmap_linear") == 0) {
+    return FT_nearest_mipmap_linear;
+  } else if (cmp_nocase_uh(string, "linear_mipmap_linear") == 0) {
+    return FT_linear_mipmap_linear;
+  } else if (cmp_nocase_uh(string, "mipmap") == 0) {
+    return FT_linear_mipmap_linear;
+  } else if (cmp_nocase_uh(string, "shadow") == 0) {
+    return FT_shadow;
+  } else if (cmp_nocase_uh(string, "default") == 0) {
+    return FT_default;
+  } else {
+    return FT_invalid;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::format_wrap_mode
+//       Access: Published, Static
+//  Description: Returns the indicated WrapMode converted to a
+//               string word.
+////////////////////////////////////////////////////////////////////
+string SamplerState::
+format_wrap_mode(WrapMode wm) {
+  switch (wm) {
+  case WM_clamp:
+    return "clamp";
+  case WM_repeat:
+    return "repeat";
+  case WM_mirror:
+    return "mirror";
+  case WM_mirror_once:
+    return "mirror_once";
+  case WM_border_color:
+    return "border_color";
+
+  case WM_invalid:
+    return "invalid";
+  }
+
+  return "**invalid**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::string_wrap_mode
+//       Access: Public
+//  Description: Returns the WrapMode value associated with the given
+//               string representation, or WM_invalid if the string
+//               does not match any known WrapMode value.
+////////////////////////////////////////////////////////////////////
+SamplerState::WrapMode SamplerState::
+string_wrap_mode(const string &string) {
+  if (cmp_nocase_uh(string, "repeat") == 0 ||
+      cmp_nocase_uh(string, "wrap") == 0) {
+    return WM_repeat;
+  } else if (cmp_nocase_uh(string, "clamp") == 0) {
+    return WM_clamp;
+  } else if (cmp_nocase_uh(string, "mirror") == 0 ||
+             cmp_nocase_uh(string, "mirrored_repeat") == 0) {
+    return WM_mirror;
+  } else if (cmp_nocase_uh(string, "mirror_once") == 0) {
+    return WM_mirror_once;
+  } else if (cmp_nocase_uh(string, "border_color") == 0 ||
+             cmp_nocase_uh(string, "border") == 0) {
+    return WM_border_color;
+  } else {
+    return WM_invalid;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::prepare
+//       Access: Published
+//  Description: Indicates that the sampler should be enqueued to be
+//               prepared in the indicated prepared_objects at the
+//               beginning of the next frame.
+//
+//               Use this function instead of prepare_now() to preload
+//               samplers from a user interface standpoint.
+////////////////////////////////////////////////////////////////////
+void SamplerState::
+prepare(PreparedGraphicsObjects *prepared_objects) const {
+  prepared_objects->enqueue_sampler(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::is_prepared
+//       Access: Published
+//  Description: Returns true if the sampler has already been prepared
+//               or enqueued for preparation on the indicated GSG,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool SamplerState::
+is_prepared(PreparedGraphicsObjects *prepared_objects) const {
+  return prepared_objects->is_sampler_queued(*this)
+      || prepared_objects->is_sampler_prepared(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::release
+//       Access: Published
+//  Description: Frees the texture context only on the indicated object,
+//               if it exists there.  Returns true if it was released,
+//               false if it had not been prepared.
+////////////////////////////////////////////////////////////////////
+void SamplerState::
+release(PreparedGraphicsObjects *prepared_objects) const {
+  prepared_objects->release_sampler(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::prepare_now
+//       Access: Published
+//  Description: Creates a context for the sampler on the particular
+//               GSG, if it does not already exist.  Returns the new
+//               (or old) SamplerContext.  This assumes that the
+//               GraphicsStateGuardian is the currently active
+//               rendering context and that it is ready to accept new
+//               textures.  If this is not necessarily the case, you
+//               should use prepare() instead.
+//
+//               Normally, this is not called directly except by the
+//               GraphicsStateGuardian; a sampler does not need to be
+//               explicitly prepared by the user before it may be
+//               rendered.
+////////////////////////////////////////////////////////////////////
+SamplerContext *SamplerState::
+prepare_now(PreparedGraphicsObjects *prepared_objects,
+            GraphicsStateGuardianBase *gsg) const {
+  return prepared_objects->prepare_sampler_now(*this, gsg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::compare_to
+//       Access: Public
+//  Description: Returns a number less than zero if this sampler
+//               sorts before the other one, greater than zero if it
+//               sorts after, or zero if they are equivalent.  The
+//               sorting order is arbitrary and largely meaningless,
+//               except to differentiate different sampler states.
+////////////////////////////////////////////////////////////////////
+int SamplerState::
+compare_to(const SamplerState &other) const {
+  if (_wrap_u != other._wrap_u) {
+    return (_wrap_u < other._wrap_u) ? -1 : 1;
+  }
+  if (_wrap_v != other._wrap_v) {
+    return (_wrap_v < other._wrap_v) ? -1 : 1;
+  }
+  if (_wrap_w != other._wrap_w) {
+    return (_wrap_w < other._wrap_w) ? -1 : 1;
+  }
+  if (_minfilter != other._minfilter) {
+    return (_minfilter < other._minfilter) ? -1 : 1;
+  }
+  if (_magfilter != other._magfilter) {
+    return (_magfilter < other._magfilter) ? -1 : 1;
+  }
+  if (_anisotropic_degree != other._anisotropic_degree) {
+    return (_anisotropic_degree < other._anisotropic_degree) ? -1 : 1;
+  }
+  if (_border_color != other._border_color) {
+    return (_border_color < other._border_color) ? -1 : 1;
+  }
+  if (_min_lod != other._min_lod) {
+    return (_min_lod < other._min_lod) ? -1 : 1;
+  }
+  if (_max_lod != other._max_lod) {
+    return (_max_lod < other._max_lod) ? -1 : 1;
+  }
+  if (_lod_bias != other._lod_bias) {
+    return (_lod_bias < other._lod_bias) ? -1 : 1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::output
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void SamplerState::
+output(ostream &out) const {
+  out
+    << "sampler"
+    << " wrap(u=" << _wrap_u << ", v=" << _wrap_v << ", w=" << _wrap_w
+    << ", border=" << _border_color << ")"
+    << " filter(min=" << _minfilter << ", mag=" << _magfilter
+    << ", aniso=" << _anisotropic_degree << ")"
+    << " lod(min=" << _min_lod << ", max=" << _max_lod
+    << ", bias=" << _lod_bias << ")";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::write
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void SamplerState::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << "SamplerState\n";
+  indent(out, indent_level) << "  wrap_u = " << _wrap_u << "\n";
+  indent(out, indent_level) << "  wrap_v = " << _wrap_v << "\n";
+  indent(out, indent_level) << "  wrap_w = " << _wrap_w << "\n";
+  indent(out, indent_level) << "  minfilter = " << _minfilter << "\n";
+  indent(out, indent_level) << "  magfilter = " << _magfilter << "\n";
+  indent(out, indent_level) << "  anisotropic_degree = " << _anisotropic_degree << "\n";
+  indent(out, indent_level) << "  border_color = " << _border_color << "\n";
+  indent(out, indent_level) << "  min_lod = " << _min_lod << "\n";
+  indent(out, indent_level) << "  max_lod = " << _max_lod << "\n";
+  indent(out, indent_level) << "  lod_bias = " << _lod_bias << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::write_datagram
+//       Access: Public
+//  Description: Encodes the sampler state into a datagram.
+////////////////////////////////////////////////////////////////////
+void SamplerState::
+write_datagram(Datagram &me) const {
+  me.add_uint8(_wrap_u);
+  me.add_uint8(_wrap_v);
+  me.add_uint8(_wrap_w);
+  me.add_uint8(_minfilter);
+  me.add_uint8(_magfilter);
+  me.add_int16(_anisotropic_degree);
+  _border_color.write_datagram(me);
+  me.add_stdfloat(_min_lod);
+  me.add_stdfloat(_max_lod);
+  me.add_stdfloat(_lod_bias);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SamplerState::read_datagram
+//       Access: Protected
+//  Description: Reads the sampler state from the datagram that has
+//               been previously written using write_datagram.
+////////////////////////////////////////////////////////////////////
+void SamplerState::
+read_datagram(DatagramIterator &scan, BamReader *manager) {
+  _wrap_u = (WrapMode)scan.get_uint8();
+  _wrap_v = (WrapMode)scan.get_uint8();
+  _wrap_w = (WrapMode)scan.get_uint8();
+  _minfilter = (FilterType)scan.get_uint8();
+  _magfilter = (FilterType)scan.get_uint8();
+  _anisotropic_degree = scan.get_int16();
+  _border_color.read_datagram(scan);
+
+  if (manager->get_file_minor_ver() >= 36) {
+    // These were added with the introduction of SamplerState.
+    // Since Texture::do_fillin_body calls this, we still have to
+    // preserve backward compatibility here.
+    _min_lod = scan.get_stdfloat();
+    _max_lod = scan.get_stdfloat();
+    _lod_bias = scan.get_stdfloat();
+  } else {
+    _min_lod = -1000;
+    _max_lod = 1000;
+    _lod_bias = 0;
+  }
+}

+ 210 - 0
panda/src/gobj/samplerState.h

@@ -0,0 +1,210 @@
+// Filename: samplerState.h
+// Created by:  rdb (09Dec14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef SAMPLERSTATE_H
+#define SAMPLERSTATE_H
+
+#include "pandabase.h"
+
+#include "typedObject.h"
+#include "namable.h"
+#include "luse.h"
+#include "numeric_types.h"
+#include "config_gobj.h"
+
+class FactoryParams;
+class GraphicsStateGuardianBase;
+class PreparedGraphicsObjects;
+class SamplerContext;
+
+////////////////////////////////////////////////////////////////////
+//       Class : SamplerState
+// Description : Represents a set of settings that indicate how a
+//               texture is sampled.  This can be used to sample the
+//               same texture using different settings in different
+//               places.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GOBJ SamplerState {
+PUBLISHED:
+  enum FilterType {
+    // Mag Filter and Min Filter
+
+    // Point sample the pixel
+    FT_nearest,
+
+    // Bilinear filtering of four neighboring pixels
+    FT_linear,
+
+    // Min Filter Only
+
+    // Point sample the pixel from the nearest mipmap level
+    FT_nearest_mipmap_nearest,
+
+    // Bilinear filter the pixel from the nearest mipmap level
+    FT_linear_mipmap_nearest,
+
+    // Point sample the pixel from two mipmap levels, and linearly blend
+    FT_nearest_mipmap_linear,
+
+    // A.k.a. trilinear filtering: Bilinear filter the pixel from
+    // two mipmap levels, and linearly blend the results.
+    FT_linear_mipmap_linear,
+
+    // The OpenGL ARB_shadow extension can be thought of as a kind of filtering.
+    FT_shadow,
+
+    // Default is usually linear, but it depends on format.
+    // This was added at the end of the list to avoid bumping TXO version #.
+    FT_default,
+
+    // Returned by string_filter_type() for an invalid match.
+    FT_invalid
+  };
+
+  enum WrapMode {
+    WM_clamp,  // coords that would be outside [0-1] are clamped to 0 or 1
+    WM_repeat,
+    WM_mirror,
+    WM_mirror_once,   // mirror once, then clamp
+    WM_border_color,  // coords outside [0-1] use explicit border color
+    // Returned by string_wrap_mode() for an invalid match.
+    WM_invalid
+  };
+
+  INLINE SamplerState();
+  INLINE static const SamplerState &get_default();
+
+  INLINE void set_wrap_u(WrapMode wrap);
+  INLINE void set_wrap_v(WrapMode wrap);
+  INLINE void set_wrap_w(WrapMode wrap);
+  INLINE void set_minfilter(FilterType filter);
+  INLINE void set_magfilter(FilterType filter);
+  INLINE void set_anisotropic_degree(int anisotropic_degree);
+  INLINE void set_border_color(const LColor &color);
+  INLINE void set_min_lod(PN_stdfloat min_lod);
+  INLINE void set_max_lod(PN_stdfloat max_lod);
+  INLINE void set_lod_bias(PN_stdfloat lod_bias);
+
+  INLINE WrapMode get_wrap_u() const;
+  INLINE WrapMode get_wrap_v() const;
+  INLINE WrapMode get_wrap_w() const;
+  INLINE FilterType get_minfilter() const;
+  INLINE FilterType get_magfilter() const;
+  FilterType get_effective_minfilter() const;
+  FilterType get_effective_magfilter() const;
+  INLINE int get_anisotropic_degree() const;
+  INLINE int get_effective_anisotropic_degree() const;
+  INLINE const LColor &get_border_color() const;
+  INLINE PN_stdfloat get_min_lod() const;
+  INLINE PN_stdfloat get_max_lod() const;
+  INLINE PN_stdfloat get_lod_bias() const;
+
+  static string format_filter_type(FilterType ft);
+  static FilterType string_filter_type(const string &str);
+
+  static string format_wrap_mode(WrapMode wm);
+  static WrapMode string_wrap_mode(const string &str);
+
+  INLINE bool operator == (const SamplerState &other) const;
+  INLINE bool operator != (const SamplerState &other) const;
+  INLINE bool operator < (const SamplerState &other) const;
+
+  void prepare(PreparedGraphicsObjects *prepared_objects) const;
+  bool is_prepared(PreparedGraphicsObjects *prepared_objects) const;
+  void release(PreparedGraphicsObjects *prepared_objects) const;
+
+  SamplerContext *prepare_now(PreparedGraphicsObjects *prepared_objects,
+                              GraphicsStateGuardianBase *gsg) const;
+
+public:
+  int compare_to(const SamplerState &other) const;
+
+  void output(ostream &out) const;
+  void write(ostream &out, int indent) const;
+
+private:
+  LColor _border_color;
+  PN_stdfloat _min_lod;
+  PN_stdfloat _max_lod;
+  PN_stdfloat _lod_bias;
+
+  // These are packed in a way that this class conveniently fits in
+  // 32 bytes; feel free to change the packing as necessary when
+  // more enum values are added.
+  FilterType _minfilter : 4;
+  FilterType _magfilter : 4;
+  WrapMode _wrap_u : 4;
+  WrapMode _wrap_v : 4;
+  WrapMode _wrap_w : 4;
+  int _anisotropic_degree : 12;
+
+  static SamplerState _default;
+
+public:
+  void write_datagram(Datagram &destination) const;
+  void read_datagram(DatagramIterator &source, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedObject::init_type();
+    register_type(_type_handle, "SamplerState",
+                  TypedObject::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;
+};
+
+extern EXPCL_PANDA_GOBJ ConfigVariableEnum<SamplerState::FilterType> texture_minfilter;
+extern EXPCL_PANDA_GOBJ ConfigVariableEnum<SamplerState::FilterType> texture_magfilter;
+extern EXPCL_PANDA_GOBJ ConfigVariableInt texture_anisotropic_degree;
+
+INLINE ostream &operator << (ostream &out, const SamplerState &m) {
+  m.output(out);
+  return out;
+}
+
+INLINE ostream &operator << (ostream &out, SamplerState::FilterType ft) {
+  return out << SamplerState::format_filter_type(ft);
+}
+
+INLINE istream &operator >> (istream &in, SamplerState::FilterType &ft) {
+  string word;
+  in >> word;
+  ft = SamplerState::string_filter_type(word);
+  return in;
+}
+
+INLINE ostream &operator << (ostream &out, SamplerState::WrapMode wm) {
+  return out << SamplerState::format_wrap_mode(wm);
+}
+
+INLINE istream &operator >> (istream &in, SamplerState::WrapMode &wm) {
+  string word;
+  in >> word;
+  wm = SamplerState::string_wrap_mode(word);
+  return in;
+}
+
+#include "samplerState.I"
+
+#endif

+ 163 - 47
panda/src/gobj/texture.I

@@ -62,7 +62,7 @@ setup_texture(Texture::TextureType texture_type, int x_size, int y_size,
               int z_size, Texture::ComponentType component_type,
               Texture::Format format) {
   CDWriter cdata(_cycler, true);
-  do_setup_texture(cdata, texture_type, x_size, y_size, z_size, 
+  do_setup_texture(cdata, texture_type, x_size, y_size, z_size,
                    component_type, format);
 }
 
@@ -112,7 +112,7 @@ setup_2d_texture() {
 //               image data.
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
-setup_2d_texture(int x_size, int y_size, ComponentType component_type, 
+setup_2d_texture(int x_size, int y_size, ComponentType component_type,
                  Format format) {
   setup_texture(TT_2d_texture, x_size, y_size, 1, component_type, format);
 }
@@ -191,7 +191,7 @@ setup_cube_map() {
 //  Description: Sets the texture as an empty cube map texture with
 //               the specified dimensions and properties.  Follow up
 //               with set_ram_image() or modify_ram_image() to fill
-//               the image data.  
+//               the image data.
 //
 //               Note that a cube map should always consist of six
 //               square images, so x_size and y_size will be the same,
@@ -273,7 +273,7 @@ write(const Filename &fullpath) {
 //               the page index.
 ////////////////////////////////////////////////////////////////////
 INLINE bool Texture::
-write(const Filename &fullpath, int z, int n, 
+write(const Filename &fullpath, int z, int n,
       bool write_pages, bool write_mipmaps) {
   CDWriter cdata(_cycler, false);
   return do_write(cdata, fullpath, z, n, write_pages, write_mipmaps);
@@ -523,7 +523,7 @@ has_alpha_fullpath() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::get_alpha_fullpath
 //       Access: Published
-//  Description: 
+//  Description:
 //               Returns the alpha_fullpath that has been set.  This
 //               is the full path to the alpha part of the image file
 //               as it was found along the texture search path.
@@ -594,7 +594,7 @@ get_num_views() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::get_num_pages
 //       Access: Published
-//  Description: Returns the total number of pages in the texture.  
+//  Description: Returns the total number of pages in the texture.
 //               Each "page" is a 2-d texture image within the larger
 //               image--a face of a cube map, or a level of a 3-d
 //               texture.  Normally, get_num_pages() is the same as
@@ -795,10 +795,17 @@ get_component_type() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_wrap_u
 //       Access: Published
-//  Description:
+//  Description: This setting determines what happens when the
+//               texture is sampled with a U value outside the range
+//               0.0-1.0.  The default is WM_repeat, which indicates
+//               that the texture should repeat indefinitely.
+//
+//               This sets the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
-set_wrap_u(Texture::WrapMode wrap) {
+set_wrap_u(SamplerState::WrapMode wrap) {
   CDWriter cdata(_cycler, true);
   do_set_wrap_u(cdata, wrap);
 }
@@ -806,10 +813,17 @@ set_wrap_u(Texture::WrapMode wrap) {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_wrap_v
 //       Access: Published
-//  Description:
+//  Description: This setting determines what happens when the
+//               texture is sampled with a V value outside the range
+//               0.0-1.0.  The default is WM_repeat, which indicates
+//               that the texture should repeat indefinitely.
+//
+//               This sets the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
-set_wrap_v(Texture::WrapMode wrap) {
+set_wrap_v(SamplerState::WrapMode wrap) {
   CDWriter cdata(_cycler, true);
   do_set_wrap_v(cdata, wrap);
 }
@@ -818,9 +832,13 @@ set_wrap_v(Texture::WrapMode wrap) {
 //     Function: Texture::set_wrap_w
 //       Access: Published
 //  Description: The W wrap direction is only used for 3-d textures.
+//
+//               This sets the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
-set_wrap_w(Texture::WrapMode wrap) {
+set_wrap_w(SamplerState::WrapMode wrap) {
   CDWriter cdata(_cycler, true);
   do_set_wrap_w(cdata, wrap);
 }
@@ -828,10 +846,15 @@ set_wrap_w(Texture::WrapMode wrap) {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_minfilter
 //       Access: Published
-//  Description:
+//  Description: Sets the filtering method that should be used when
+//               viewing the texture from a distance.
+//
+//               This sets the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
-set_minfilter(Texture::FilterType filter) {
+set_minfilter(SamplerState::FilterType filter) {
   CDWriter cdata(_cycler, true);
   do_set_minfilter(cdata, filter);
 }
@@ -839,10 +862,15 @@ set_minfilter(Texture::FilterType filter) {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_magfilter
 //       Access: Published
-//  Description:
+//  Description: Sets the filtering method that should be used when
+//               viewing the texture up close.
+//
+//               This sets the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
-set_magfilter(Texture::FilterType filter) {
+set_magfilter(SamplerState::FilterType filter) {
   CDWriter cdata(_cycler, true);
   do_set_magfilter(cdata, filter);
 }
@@ -859,6 +887,10 @@ set_magfilter(Texture::FilterType filter) {
 //               value to 1.  To explicitly enable anisotropic
 //               filtering, set it to a value higher than 1; larger
 //               numbers indicate greater degrees of filtering.
+//
+//               This sets the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
 set_anisotropic_degree(int anisotropic_degree) {
@@ -873,6 +905,10 @@ set_anisotropic_degree(int anisotropic_degree) {
 //               Some OpenGL implementations use a border for tiling
 //               textures; in Panda, it is only used for specifying
 //               the clamp color.
+//
+//               This sets the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
 set_border_color(const LColor &color) {
@@ -884,7 +920,7 @@ set_border_color(const LColor &color) {
 //     Function: Texture::set_compression
 //       Access: Published
 //  Description: Requests that this particular Texture be compressed
-//               when it is loaded into texture memory.  
+//               when it is loaded into texture memory.
 //
 //               This refers to the internal compression of the
 //               texture image within texture memory; it is not
@@ -928,16 +964,35 @@ set_render_to_texture(bool render_to_texture) {
   cdata->_render_to_texture = render_to_texture;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::get_default_sampler
+//       Access: Published
+//  Description: This returns the default sampler state for this
+//               texture, containing the wrap and filter properties
+//               specified on the texture level; it may still be
+//               overridden by a sampler state specified at a higher
+//               level.
+////////////////////////////////////////////////////////////////////
+INLINE const SamplerState &Texture::
+get_default_sampler() const {
+  CDReader cdata(_cycler);
+  return cdata->_default_sampler;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::get_wrap_u
 //       Access: Published
 //  Description: Returns the wrap mode of the texture in the U
 //               direction.
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
-INLINE Texture::WrapMode Texture::
+INLINE SamplerState::WrapMode Texture::
 get_wrap_u() const {
   CDReader cdata(_cycler);
-  return cdata->_wrap_u;
+  return cdata->_default_sampler.get_wrap_u();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -945,11 +1000,15 @@ get_wrap_u() const {
 //       Access: Published
 //  Description: Returns the wrap mode of the texture in the V
 //               direction.
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
-INLINE Texture::WrapMode Texture::
+INLINE SamplerState::WrapMode Texture::
 get_wrap_v() const {
   CDReader cdata(_cycler);
-  return cdata->_wrap_v;
+  return cdata->_default_sampler.get_wrap_v();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -958,11 +1017,15 @@ get_wrap_v() const {
 //  Description: Returns the wrap mode of the texture in the W
 //               direction.  This is the depth direction of 3-d
 //               textures.
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
-INLINE Texture::WrapMode Texture::
+INLINE SamplerState::WrapMode Texture::
 get_wrap_w() const {
   CDReader cdata(_cycler);
-  return cdata->_wrap_w;
+  return cdata->_default_sampler.get_wrap_w();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -973,11 +1036,15 @@ get_wrap_w() const {
 //               constants, then the texture requires mipmaps.  This
 //               may return FT_default; see also
 //               get_effective_minfilter().
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
-INLINE Texture::FilterType Texture::
+INLINE SamplerState::FilterType Texture::
 get_minfilter() const {
   CDReader cdata(_cycler);
-  return cdata->_minfilter;
+  return cdata->_default_sampler.get_minfilter();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -987,11 +1054,51 @@ get_minfilter() const {
 //               magnification.  The mipmap constants are invalid
 //               here.  This may return FT_default; see also
 //               get_effective_minfilter().
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
-INLINE Texture::FilterType Texture::
+INLINE SamplerState::FilterType Texture::
 get_magfilter() const {
   CDReader cdata(_cycler);
-  return cdata->_magfilter;
+  return cdata->_default_sampler.get_magfilter();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::get_effective_minfilter
+//       Access: Published
+//  Description: Returns the filter mode of the texture for
+//               minification, with special treatment for FT_default.
+//               This will normally not return FT_default, unless
+//               there is an error in the config file.
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
+////////////////////////////////////////////////////////////////////
+SamplerState::FilterType Texture::
+get_effective_minfilter() const {
+  CDReader cdata(_cycler);
+  return cdata->_default_sampler.get_effective_minfilter();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::get_effective_magfilter
+//       Access: Published
+//  Description: Returns the filter mode of the texture for
+//               magnification, with special treatment for FT_default.
+//               This will normally not return FT_default, unless
+//               there is an error in the config file.
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
+////////////////////////////////////////////////////////////////////
+SamplerState::FilterType Texture::
+get_effective_magfilter() const {
+  CDReader cdata(_cycler);
+  return cdata->_default_sampler.get_effective_magfilter();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1001,11 +1108,15 @@ get_magfilter() const {
 //               should be applied to the texture.  This value may
 //               return 0, indicating the default value; see also
 //               get_effective_anisotropic_degree.
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE int Texture::
 get_anisotropic_degree() const {
   CDReader cdata(_cycler);
-  return cdata->_anisotropic_degree;
+  return cdata->_default_sampler.get_anisotropic_degree();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1015,14 +1126,15 @@ get_anisotropic_degree() const {
 //               should be applied to the texture.  This value will
 //               normally not return 0, unless there is an error in
 //               the config file.
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE int Texture::
 get_effective_anisotropic_degree() const {
   CDReader cdata(_cycler);
-  if (cdata->_anisotropic_degree != 0) {
-    return cdata->_anisotropic_degree;
-  }
-  return texture_anisotropic_degree;
+  return cdata->_default_sampler.get_effective_anisotropic_degree();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1032,11 +1144,15 @@ get_effective_anisotropic_degree() const {
 //               Some OpenGL implementations use a border for tiling
 //               textures; in Panda, it is only used for specifying
 //               the clamp color.
+//
+//               This returns the default sampler state for this
+//               texture; it may still be overridden by a sampler
+//               state specified at a higher level.
 ////////////////////////////////////////////////////////////////////
 INLINE LColor Texture::
 get_border_color() const {
   CDReader cdata(_cycler);
-  return cdata->_border_color;
+  return cdata->_default_sampler.get_border_color();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2400,7 +2516,7 @@ adjust_this_size(int &x_size, int &y_size, const string &name,
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_get_ram_image_size
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE size_t Texture::
 do_get_ram_image_size(const CData *cdata) const {
@@ -2413,18 +2529,18 @@ do_get_ram_image_size(const CData *cdata) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_has_ram_mipmap_image
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool Texture::
 do_has_ram_mipmap_image(const CData *cdata, int n) const {
-  return (n >= 0 && n < (int)cdata->_ram_images.size() && 
+  return (n >= 0 && n < (int)cdata->_ram_images.size() &&
           !cdata->_ram_images[n]._image.empty());
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_get_expected_ram_image_size
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE size_t Texture::
 do_get_expected_ram_image_size(const CData *cdata) const {
@@ -2434,7 +2550,7 @@ do_get_expected_ram_image_size(const CData *cdata) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_get_expected_ram_view_size
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE size_t Texture::
 do_get_expected_ram_view_size(const CData *cdata) const {
@@ -2444,7 +2560,7 @@ do_get_expected_ram_view_size(const CData *cdata) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_get_expected_ram_page_size
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE size_t Texture::
 do_get_expected_ram_page_size(const CData *cdata) const {
@@ -2454,7 +2570,7 @@ do_get_expected_ram_page_size(const CData *cdata) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_get_expected_ram_mipmap_image_size
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE size_t Texture::
 do_get_expected_ram_mipmap_image_size(const CData *cdata, int n) const {
@@ -2464,7 +2580,7 @@ do_get_expected_ram_mipmap_image_size(const CData *cdata, int n) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_get_expected_ram_mipmap_view_size
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE size_t Texture::
 do_get_expected_ram_mipmap_view_size(const CData *cdata, int n) const {
@@ -2474,7 +2590,7 @@ do_get_expected_ram_mipmap_view_size(const CData *cdata, int n) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_get_expected_ram_mipmap_page_size
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE size_t Texture::
 do_get_expected_ram_mipmap_page_size(const CData *cdata, int n) const {
@@ -2494,7 +2610,7 @@ do_get_expected_mipmap_num_pages(const CData *cdata, int n) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_clear_ram_image
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
 do_clear_ram_image(CData *cdata) {
@@ -2505,7 +2621,7 @@ do_clear_ram_image(CData *cdata) {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::do_get_auto_texture_scale
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE AutoTextureScale Texture::
 do_get_auto_texture_scale(const CData *cdata) const {
@@ -2647,7 +2763,7 @@ is_dds_filename(const Filename &fullpath) {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::CData::inc_properties_modified
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::CData::
 inc_properties_modified() {
@@ -2657,7 +2773,7 @@ inc_properties_modified() {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::CData::inc_image_modified
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::CData::
 inc_image_modified() {
@@ -2667,7 +2783,7 @@ inc_image_modified() {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::CData::inc_simple_image_modified
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::CData::
 inc_simple_image_modified() {
@@ -2677,7 +2793,7 @@ inc_simple_image_modified() {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::RamImage::Constructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE Texture::RamImage::
 RamImage() :

+ 70 - 355
panda/src/gobj/texture.cxx

@@ -58,36 +58,6 @@ ConfigVariableEnum<Texture::QualityLevel> texture_quality_level
           "it has little or no effect on normal, hardware-accelerated "
           "renderers.  See Texture::set_quality_level()."));
 
-ConfigVariableEnum<Texture::FilterType> texture_minfilter
-("texture-minfilter", Texture::FT_linear,
- PRC_DESC("This specifies the default minfilter that is applied to a texture "
-          "in the absence of a specific minfilter setting.  Normally this "
-          "is either 'linear' to disable mipmapping by default, or "
-          "'mipmap', to enable trilinear mipmapping by default.  This "
-          "does not apply to depth textures.  Note if this variable is "
-          "changed at runtime, you may need to reload textures explicitly "
-          "in order to change their visible properties."));
-
-ConfigVariableEnum<Texture::FilterType> texture_magfilter
-("texture-magfilter", Texture::FT_linear,
- PRC_DESC("This specifies the default magfilter that is applied to a texture "
-          "in the absence of a specific magfilter setting.  Normally this "
-          "is 'linear' (since mipmapping does not apply to magfilters).  This "
-          "does not apply to depth textures.  Note if this variable is "
-          "changed at runtime, you may need to reload textures explicitly "
-          "in order to change their visible properties."));
-
-ConfigVariableInt texture_anisotropic_degree
-("texture-anisotropic-degree", 1,
- PRC_DESC("This specifies the default anisotropic degree that is applied "
-          "to a texture in the absence of a particular anisotropic degree "
-          "setting (that is, a texture for which the anisotropic degree "
-          "is 0, meaning the default setting).  It should be 1 to disable "
-          "anisotropic filtering, or a higher number to enable it.  "
-          "Note if this variable is "
-          "changed at runtime, you may need to reload textures explicitly "
-          "in order to change their visible properties."));
-
 PStatCollector Texture::_texture_read_pcollector("*:Texture:Read");
 TypeHandle Texture::_type_handle;
 TypeHandle Texture::CData::_type_handle;
@@ -380,9 +350,10 @@ void Texture::
 generate_alpha_scale_map() {
   CDWriter cdata(_cycler, true);
   do_setup_texture(cdata, TT_1d_texture, 256, 1, 1, T_unsigned_byte, F_alpha);
-  cdata->_wrap_u = WM_clamp;
-  cdata->_minfilter = FT_nearest;
-  cdata->_magfilter = FT_nearest;
+  cdata->_default_sampler.set_wrap_u(SamplerState::WM_clamp);
+  cdata->_default_sampler.set_minfilter(SamplerState::FT_nearest);
+  cdata->_default_sampler.set_magfilter(SamplerState::FT_nearest);
+
   cdata->_compression = CM_off;
 
   cdata->inc_image_modified();
@@ -877,48 +848,6 @@ load_related(const InternalName *suffix) const {
   return res;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::get_effective_minfilter
-//       Access: Published
-//  Description: Returns the filter mode of the texture for
-//               minification, with special treatment for FT_default.
-//               This will normally not return FT_default, unless
-//               there is an error in the config file.
-////////////////////////////////////////////////////////////////////
-Texture::FilterType Texture::
-get_effective_minfilter() const {
-  CDReader cdata(_cycler);
-  if (cdata->_minfilter != FT_default) {
-    return cdata->_minfilter;
-  }
-  if (cdata->_format == Texture::F_depth_stencil ||
-      cdata->_format == Texture::F_depth_component) {
-    return FT_nearest;
-  }
-  return texture_minfilter;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::get_effective_magfilter
-//       Access: Published
-//  Description: Returns the filter mode of the texture for
-//               magnification, with special treatment for FT_default.
-//               This will normally not return FT_default, unless
-//               there is an error in the config file.
-////////////////////////////////////////////////////////////////////
-Texture::FilterType Texture::
-get_effective_magfilter() const {
-  CDReader cdata(_cycler);
-  if (cdata->_magfilter != FT_default) {
-    return cdata->_magfilter;
-  }
-  if (cdata->_format == Texture::F_depth_stencil ||
-      cdata->_format == Texture::F_depth_component) {
-    return FT_nearest;
-  }
-  return texture_magfilter;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_ram_image_as
 //       Access: Published
@@ -1789,32 +1718,7 @@ write(ostream &out, int indent_level) const {
 
   indent(out, indent_level + 2);
 
-  switch (cdata->_texture_type) {
-  case TT_1d_texture:
-    out << cdata->_wrap_u << ", ";
-    break;
-
-  case TT_2d_texture:
-    out << cdata->_wrap_u << " x " << cdata->_wrap_v << ", ";
-    break;
-
-  case TT_3d_texture:
-    out << cdata->_wrap_u << " x " << cdata->_wrap_v << " x " << cdata->_wrap_w << ", ";
-    break;
-
-  case TT_2d_texture_array:
-    out << cdata->_wrap_u << " x " << cdata->_wrap_v << " x " << cdata->_wrap_w << ", ";
-    break;
-
-  case TT_cube_map:
-    break;
-  }
-
-  out << "min " << cdata->_minfilter
-      << ", mag " << cdata->_magfilter
-      << ", aniso " << cdata->_anisotropic_degree
-      << ", border " << cdata->_border_color
-      << "\n";
+  cdata->_default_sampler.output(out);
 
   if (do_has_ram_image(cdata)) {
     indent(out, indent_level + 2)
@@ -1879,7 +1783,7 @@ set_size_padded(int x, int y, int z) {
     do_set_y_size(cdata, y);
     do_set_z_size(cdata, z);
   }
-  do_set_pad_size(cdata, 
+  do_set_pad_size(cdata,
                   cdata->_x_size - x,
                   cdata->_y_size - y,
                   cdata->_z_size - z);
@@ -1909,10 +1813,10 @@ set_orig_file_size(int x, int y, int z) {
 bool Texture::
 is_mipmap(FilterType filter_type) {
   switch (filter_type) {
-  case FT_nearest_mipmap_nearest:
-  case FT_linear_mipmap_nearest:
-  case FT_nearest_mipmap_linear:
-  case FT_linear_mipmap_linear:
+  case SamplerState::FT_nearest_mipmap_nearest:
+  case SamplerState::FT_linear_mipmap_nearest:
+  case SamplerState::FT_nearest_mipmap_linear:
+  case SamplerState::FT_linear_mipmap_linear:
     return true;
 
   default:
@@ -2315,127 +2219,6 @@ string_format(const string &str) {
   return F_rgba;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::format_filter_type
-//       Access: Published, Static
-//  Description: Returns the indicated FilterType converted to a
-//               string word.
-////////////////////////////////////////////////////////////////////
-string Texture::
-format_filter_type(FilterType ft) {
-  switch (ft) {
-  case FT_nearest:
-    return "nearest";
-  case FT_linear:
-    return "linear";
-
-  case FT_nearest_mipmap_nearest:
-    return "nearest_mipmap_nearest";
-  case FT_linear_mipmap_nearest:
-    return "linear_mipmap_nearest";
-  case FT_nearest_mipmap_linear:
-    return "nearest_mipmap_linear";
-  case FT_linear_mipmap_linear:
-    return "linear_mipmap_linear";
-
-  case FT_shadow:
-    return "shadow";
-
-  case FT_default:
-    return "default";
-
-  case FT_invalid:
-    return "invalid";
-  }
-  return "**invalid**";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::string_filter_type
-//       Access: Public
-//  Description: Returns the FilterType value associated with the given
-//               string representation, or FT_invalid if the string
-//               does not match any known FilterType value.
-////////////////////////////////////////////////////////////////////
-Texture::FilterType Texture::
-string_filter_type(const string &string) {
-  if (cmp_nocase_uh(string, "nearest") == 0) {
-    return FT_nearest;
-  } else if (cmp_nocase_uh(string, "linear") == 0) {
-    return FT_linear;
-  } else if (cmp_nocase_uh(string, "nearest_mipmap_nearest") == 0) {
-    return FT_nearest_mipmap_nearest;
-  } else if (cmp_nocase_uh(string, "linear_mipmap_nearest") == 0) {
-    return FT_linear_mipmap_nearest;
-  } else if (cmp_nocase_uh(string, "nearest_mipmap_linear") == 0) {
-    return FT_nearest_mipmap_linear;
-  } else if (cmp_nocase_uh(string, "linear_mipmap_linear") == 0) {
-    return FT_linear_mipmap_linear;
-  } else if (cmp_nocase_uh(string, "mipmap") == 0) {
-    return FT_linear_mipmap_linear;
-  } else if (cmp_nocase_uh(string, "shadow") == 0) {
-    return FT_shadow;
-  } else if (cmp_nocase_uh(string, "default") == 0) {
-    return FT_default;
-  } else {
-    return FT_invalid;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::format_wrap_mode
-//       Access: Published, Static
-//  Description: Returns the indicated WrapMode converted to a
-//               string word.
-////////////////////////////////////////////////////////////////////
-string Texture::
-format_wrap_mode(WrapMode wm) {
-  switch (wm) {
-  case WM_clamp:
-    return "clamp";
-  case WM_repeat:
-    return "repeat";
-  case WM_mirror:
-    return "mirror";
-  case WM_mirror_once:
-    return "mirror_once";
-  case WM_border_color:
-    return "border_color";
-
-  case WM_invalid:
-    return "invalid";
-  }
-
-  return "**invalid**";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::string_wrap_mode
-//       Access: Public
-//  Description: Returns the WrapMode value associated with the given
-//               string representation, or WM_invalid if the string
-//               does not match any known WrapMode value.
-////////////////////////////////////////////////////////////////////
-Texture::WrapMode Texture::
-string_wrap_mode(const string &string) {
-  if (cmp_nocase_uh(string, "repeat") == 0 ||
-      cmp_nocase_uh(string, "wrap") == 0) {
-    return WM_repeat;
-  } else if (cmp_nocase_uh(string, "clamp") == 0) {
-    return WM_clamp;
-  } else if (cmp_nocase_uh(string, "mirror") == 0 ||
-             cmp_nocase_uh(string, "mirrored_repeat") == 0) {
-    return WM_mirror;
-  } else if (cmp_nocase_uh(string, "mirror_once") == 0) {
-    return WM_mirror_once;
-  } else if (cmp_nocase_uh(string, "border_color") == 0 ||
-             cmp_nocase_uh(string, "border") == 0) {
-    return WM_border_color;
-  } else {
-    return WM_invalid;
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::format_compression_mode
 //       Access: Published, Static
@@ -3955,7 +3738,7 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only)
 //               mipmap levels to disk files.
 ////////////////////////////////////////////////////////////////////
 bool Texture::
-do_write(CData *cdata, 
+do_write(CData *cdata,
          const Filename &fullpath, int z, int n, bool write_pages, bool write_mipmaps) {
   if (is_txo_filename(fullpath)) {
     if (!do_has_bam_rawdata(cdata)) {
@@ -4273,20 +4056,20 @@ unlocked_ensure_ram_image(bool allow_compression) {
   // We need to reload.
   nassertr(!_reloading, NULL);
   _reloading = true;
-  
+
   PT(Texture) tex = do_make_copy(cdata);
   _cycler.release_read(cdata);
   _lock.release();
-  
+
   // Perform the actual reload in a copy of the texture, while our
   // own mutex is left unlocked.
   CDWriter cdata_tex(tex->_cycler, true);
   tex->do_reload_ram_image(cdata_tex, allow_compression);
-  
+
   _lock.acquire();
 
   CData *cdataw = _cycler.write_upstream(false, current_thread);
-  
+
   // Rather than calling do_assign(), which would copy *all* of the
   // reloaded texture's properties over, we only copy in the ones
   // which are relevant to the ram image.  This way, if the
@@ -4295,7 +4078,7 @@ unlocked_ensure_ram_image(bool allow_compression) {
   // texture.
   cdataw->_orig_file_x_size = cdata_tex->_orig_file_x_size;
   cdataw->_orig_file_y_size = cdata_tex->_orig_file_y_size;
-  
+
   // If any of *these* properties have changed, the texture has
   // changed in some fundamental way.  Update it appropriately.
   if (cdata_tex->_x_size != cdataw->_x_size ||
@@ -4306,33 +4089,33 @@ unlocked_ensure_ram_image(bool allow_compression) {
       cdata_tex->_component_width != cdataw->_component_width ||
       cdata_tex->_texture_type != cdataw->_texture_type ||
       cdata_tex->_component_type != cdataw->_component_type) {
-    
+
     cdataw->_x_size = cdata_tex->_x_size;
     cdataw->_y_size = cdata_tex->_y_size;
     cdataw->_z_size = cdata_tex->_z_size;
     cdataw->_num_views = cdata_tex->_num_views;
-    
+
     cdataw->_num_components = cdata_tex->_num_components;
     cdataw->_component_width = cdata_tex->_component_width;
     cdataw->_texture_type = cdata_tex->_texture_type;
     cdataw->_format = cdata_tex->_format;
     cdataw->_component_type = cdata_tex->_component_type;
-    
+
     cdataw->inc_properties_modified();
     cdataw->inc_image_modified();
   }
-  
+
   cdataw->_keep_ram_image = cdata_tex->_keep_ram_image;
   cdataw->_ram_image_compression = cdata_tex->_ram_image_compression;
   cdataw->_ram_images = cdata_tex->_ram_images;
-  
+
   nassertr(_reloading, NULL);
   _reloading = false;
-  
+
   // We don't generally increment the cdata->_image_modified semaphore,
   // because this is just a reload, and presumably the image hasn't
   // changed (unless we hit the if condition above).
-  
+
   _cvar.notify_all();
 
   // Return the still-locked cdata.
@@ -4626,7 +4409,7 @@ consider_auto_process_ram_image(bool generate_mipmaps, bool allow_compression) {
 //               operation, false if it wasn't.
 ////////////////////////////////////////////////////////////////////
 bool Texture::
-do_consider_auto_process_ram_image(CData *cdata, bool generate_mipmaps, 
+do_consider_auto_process_ram_image(CData *cdata, bool generate_mipmaps,
                                    bool allow_compression) {
   bool modified = false;
 
@@ -5158,10 +4941,10 @@ do_setup_texture(CData *cdata, Texture::TextureType texture_type, int x_size, in
 
     // In principle the wrap mode shouldn't mean anything to a cube
     // map, but some drivers seem to misbehave if it's other than
-    // WM_clamp.
-    cdata->_wrap_u = WM_clamp;
-    cdata->_wrap_v = WM_clamp;
-    cdata->_wrap_w = WM_clamp;
+    // SamplerState::WM_clamp.
+    cdata->_default_sampler.set_wrap_u(SamplerState::WM_clamp);
+    cdata->_default_sampler.set_wrap_v(SamplerState::WM_clamp);
+    cdata->_default_sampler.set_wrap_w(SamplerState::WM_clamp);
     break;
   }
 
@@ -5359,10 +5142,10 @@ do_set_num_views(CData *cdata, int num_views) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void Texture::
-do_set_wrap_u(CData *cdata, Texture::WrapMode wrap) {
-  if (cdata->_wrap_u != wrap) {
+do_set_wrap_u(CData *cdata, SamplerState::WrapMode wrap) {
+  if (cdata->_default_sampler.get_wrap_u() != wrap) {
     cdata->inc_properties_modified();
-    cdata->_wrap_u = wrap;
+    cdata->_default_sampler.set_wrap_u(wrap);
   }
 }
 
@@ -5372,10 +5155,10 @@ do_set_wrap_u(CData *cdata, Texture::WrapMode wrap) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void Texture::
-do_set_wrap_v(CData *cdata, Texture::WrapMode wrap) {
-  if (cdata->_wrap_v != wrap) {
+do_set_wrap_v(CData *cdata, SamplerState::WrapMode wrap) {
+  if (cdata->_default_sampler.get_wrap_v() != wrap) {
     cdata->inc_properties_modified();
-    cdata->_wrap_v = wrap;
+    cdata->_default_sampler.set_wrap_v(wrap);
   }
 }
 
@@ -5385,10 +5168,10 @@ do_set_wrap_v(CData *cdata, Texture::WrapMode wrap) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void Texture::
-do_set_wrap_w(CData *cdata, Texture::WrapMode wrap) {
-  if (cdata->_wrap_w != wrap) {
+do_set_wrap_w(CData *cdata, SamplerState::WrapMode wrap) {
+  if (cdata->_default_sampler.get_wrap_w() != wrap) {
     cdata->inc_properties_modified();
-    cdata->_wrap_w = wrap;
+    cdata->_default_sampler.set_wrap_w(wrap);
   }
 }
 
@@ -5398,10 +5181,10 @@ do_set_wrap_w(CData *cdata, Texture::WrapMode wrap) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void Texture::
-do_set_minfilter(CData *cdata, Texture::FilterType filter) {
-  if (cdata->_minfilter != filter) {
+do_set_minfilter(CData *cdata, SamplerState::FilterType filter) {
+  if (cdata->_default_sampler.get_minfilter() != filter) {
     cdata->inc_properties_modified();
-    cdata->_minfilter = filter;
+    cdata->_default_sampler.set_minfilter(filter);
   }
 }
 
@@ -5411,10 +5194,10 @@ do_set_minfilter(CData *cdata, Texture::FilterType filter) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void Texture::
-do_set_magfilter(CData *cdata, Texture::FilterType filter) {
-  if (cdata->_magfilter != filter) {
+do_set_magfilter(CData *cdata, SamplerState::FilterType filter) {
+  if (cdata->_default_sampler.get_magfilter() != filter) {
     cdata->inc_properties_modified();
-    cdata->_magfilter = filter;
+    cdata->_default_sampler.set_magfilter(filter);
   }
 }
 
@@ -5425,9 +5208,9 @@ do_set_magfilter(CData *cdata, Texture::FilterType filter) {
 ////////////////////////////////////////////////////////////////////
 void Texture::
 do_set_anisotropic_degree(CData *cdata, int anisotropic_degree) {
-  if (cdata->_anisotropic_degree != anisotropic_degree) {
+  if (cdata->_default_sampler.get_anisotropic_degree() != anisotropic_degree) {
     cdata->inc_properties_modified();
-    cdata->_anisotropic_degree = anisotropic_degree;
+    cdata->_default_sampler.set_anisotropic_degree(anisotropic_degree);
   }
 }
 
@@ -5438,9 +5221,9 @@ do_set_anisotropic_degree(CData *cdata, int anisotropic_degree) {
 ////////////////////////////////////////////////////////////////////
 void Texture::
 do_set_border_color(CData *cdata, const LColor &color) {
-  if (cdata->_border_color != color) {
+  if (cdata->_default_sampler.get_border_color() != color) {
     cdata->inc_properties_modified();
-    cdata->_border_color = color;
+    cdata->_default_sampler.set_border_color(color);
   }
 }
 
@@ -7870,7 +7653,7 @@ do_write_datagram_header(CData *cdata, BamWriter *manager, Datagram &me, bool &h
   // loads the bam file later will have access to the image file on
   // disk.
   BamWriter::BamTextureMode file_texture_mode = manager->get_file_texture_mode();
-  has_rawdata = (file_texture_mode == BamWriter::BTM_rawdata || 
+  has_rawdata = (file_texture_mode == BamWriter::BTM_rawdata ||
                  (cdata->_filename.empty() && do_has_bam_rawdata(cdata)));
   if (has_rawdata && !do_has_bam_rawdata(cdata)) {
     do_get_bam_rawdata(cdata);
@@ -7953,13 +7736,8 @@ do_write_datagram_header(CData *cdata, BamWriter *manager, Datagram &me, bool &h
 ////////////////////////////////////////////////////////////////////
 void Texture::
 do_write_datagram_body(CData *cdata, BamWriter *manager, Datagram &me) {
-  me.add_uint8(cdata->_wrap_u);
-  me.add_uint8(cdata->_wrap_v);
-  me.add_uint8(cdata->_wrap_w);
-  me.add_uint8(cdata->_minfilter);
-  me.add_uint8(cdata->_magfilter);
-  me.add_int16(cdata->_anisotropic_degree);
-  cdata->_border_color.write_datagram(me);
+  cdata->_default_sampler.write_datagram(me);
+
   me.add_uint8(cdata->_compression);
   me.add_uint8(cdata->_quality_level);
 
@@ -8181,13 +7959,7 @@ make_this_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 void Texture::
 do_fillin_body(CData *cdata, DatagramIterator &scan, BamReader *manager) {
-  cdata->_wrap_u = (WrapMode)scan.get_uint8();
-  cdata->_wrap_v = (WrapMode)scan.get_uint8();
-  cdata->_wrap_w = (WrapMode)scan.get_uint8();
-  cdata->_minfilter = (FilterType)scan.get_uint8();
-  cdata->_magfilter = (FilterType)scan.get_uint8();
-  cdata->_anisotropic_degree = scan.get_int16();
-  cdata->_border_color.read_datagram(scan);
+  cdata->_default_sampler.read_datagram(scan, manager);
 
   if (manager->get_file_minor_ver() >= 1) {
     cdata->_compression = (CompressionMode)scan.get_uint8();
@@ -8261,12 +8033,12 @@ do_fillin_rawdata(CData *cdata, DatagramIterator &scan, BamReader *manager) {
   if (manager->get_file_minor_ver() >= 1) {
     cdata->_ram_image_compression = (CompressionMode)scan.get_uint8();
   }
-  
+
   int num_ram_images = 1;
   if (manager->get_file_minor_ver() >= 3) {
     num_ram_images = scan.get_uint8();
   }
-  
+
   cdata->_ram_images.clear();
   cdata->_ram_images.reserve(num_ram_images);
   for (int n = 0; n < num_ram_images; ++n) {
@@ -8275,9 +8047,9 @@ do_fillin_rawdata(CData *cdata, DatagramIterator &scan, BamReader *manager) {
     if (manager->get_file_minor_ver() >= 1) {
       cdata->_ram_images[n]._page_size = scan.get_uint32();
     }
-    
+
     size_t u_size = scan.get_uint32();
-    
+
     // fill the cdata->_image buffer with image data
     PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
     for (size_t u_idx = 0; u_idx < u_size; ++u_idx) {
@@ -8308,19 +8080,19 @@ do_fillin_from(CData *cdata, const Texture *dummy) {
 
   CDReader cdata_dummy(dummy->_cycler);
 
-  do_set_wrap_u(cdata, cdata_dummy->_wrap_u);
-  do_set_wrap_v(cdata, cdata_dummy->_wrap_v);
-  do_set_wrap_w(cdata, cdata_dummy->_wrap_w);
-  do_set_border_color(cdata, cdata_dummy->_border_color);
+  do_set_wrap_u(cdata, cdata_dummy->_default_sampler.get_wrap_u());
+  do_set_wrap_v(cdata, cdata_dummy->_default_sampler.get_wrap_v());
+  do_set_wrap_w(cdata, cdata_dummy->_default_sampler.get_wrap_w());
+  do_set_border_color(cdata, cdata_dummy->_default_sampler.get_border_color());
 
-  if (cdata_dummy->_minfilter != FT_default) {
-    do_set_minfilter(cdata, cdata_dummy->_minfilter);
+  if (cdata_dummy->_default_sampler.get_minfilter() != SamplerState::FT_default) {
+    do_set_minfilter(cdata, cdata_dummy->_default_sampler.get_minfilter());
   }
-  if (cdata_dummy->_magfilter != FT_default) {
-    do_set_magfilter(cdata, cdata_dummy->_magfilter);
+  if (cdata_dummy->_default_sampler.get_magfilter() != SamplerState::FT_default) {
+    do_set_magfilter(cdata, cdata_dummy->_default_sampler.get_magfilter());
   }
-  if (cdata_dummy->_anisotropic_degree != 0) {
-    do_set_anisotropic_degree(cdata, cdata_dummy->_anisotropic_degree);
+  if (cdata_dummy->_default_sampler.get_anisotropic_degree() != 0) {
+    do_set_anisotropic_degree(cdata, cdata_dummy->_default_sampler.get_anisotropic_degree());
   }
   if (cdata_dummy->_compression != CM_default) {
     do_set_compression(cdata, cdata_dummy->_compression);
@@ -8345,7 +8117,7 @@ do_fillin_from(CData *cdata, const Texture *dummy) {
     // recently than the one we already have.
     if (cdata->_simple_ram_image._image.empty() ||
         cdata_dummy->_simple_image_date_generated > cdata->_simple_image_date_generated) {
-      do_set_simple_ram_image(cdata, 
+      do_set_simple_ram_image(cdata,
                               cdata_dummy->_simple_ram_image._image,
                               cdata_dummy->_simple_x_size,
                               cdata_dummy->_simple_y_size);
@@ -8357,20 +8129,13 @@ do_fillin_from(CData *cdata, const Texture *dummy) {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::CData::Constructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 Texture::CData::
 CData() {
   _primary_file_num_channels = 0;
   _alpha_file_channel = 0;
-  _magfilter = FT_default;
-  _minfilter = FT_default;
-  _wrap_u = WM_repeat;
-  _wrap_v = WM_repeat;
-  _wrap_w = WM_repeat;
-  _anisotropic_degree = 0;
   _keep_ram_image = true;
-  _border_color.set(0.0f, 0.0f, 0.0f, 1.0f);
   _compression = CM_default;
   _auto_texture_scale = ATS_unspecified;
   _ram_image_compression = CM_off;
@@ -8411,7 +8176,7 @@ CData() {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::CData::Copy Constructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 Texture::CData::
 CData(const Texture::CData &copy) {
@@ -8437,7 +8202,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::CData::do_assign
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 void Texture::CData::
 do_assign(const Texture::CData *copy) {
@@ -8470,14 +8235,8 @@ do_assign(const Texture::CData *copy) {
   _has_read_pages = copy->_has_read_pages;
   _has_read_mipmaps = copy->_has_read_mipmaps;
   _num_mipmap_levels_read = copy->_num_mipmap_levels_read;
-  _wrap_u = copy->_wrap_u;
-  _wrap_v = copy->_wrap_v;
-  _wrap_w = copy->_wrap_w;
-  _minfilter = copy->_minfilter;
-  _magfilter = copy->_magfilter;
-  _anisotropic_degree = copy->_anisotropic_degree;
+  _default_sampler = copy->_default_sampler;
   _keep_ram_image = copy->_keep_ram_image;
-  _border_color = copy->_border_color;
   _compression = copy->_compression;
   _match_framebuffer_format = copy->_match_framebuffer_format;
   _quality_level = copy->_quality_level;
@@ -8549,50 +8308,6 @@ operator << (ostream &out, Texture::Format f) {
   return out << Texture::format_format(f);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::FilterType output operator
-//  Description:
-////////////////////////////////////////////////////////////////////
-ostream &
-operator << (ostream &out, Texture::FilterType ft) {
-  return out << Texture::format_filter_type(ft);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::FilterType input operator
-//  Description:
-////////////////////////////////////////////////////////////////////
-istream &
-operator >> (istream &in, Texture::FilterType &ft) {
-  string word;
-  in >> word;
-
-  ft = Texture::string_filter_type(word);
-  return in;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::WrapMode output operator
-//  Description:
-////////////////////////////////////////////////////////////////////
-ostream &
-operator << (ostream &out, Texture::WrapMode wm) {
-  return out << Texture::format_wrap_mode(wm);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::WrapMode input operator
-//  Description:
-////////////////////////////////////////////////////////////////////
-istream &
-operator >> (istream &in, Texture::WrapMode &wm) {
-  string word;
-  in >> word;
-
-  wm = Texture::string_wrap_mode(word);
-  return in;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::CompressionMode output operator
 //  Description:

+ 83 - 122
panda/src/gobj/texture.h

@@ -39,6 +39,7 @@
 #include "cycleDataStageReader.h"
 #include "cycleDataStageWriter.h"
 #include "pipelineCycler.h"
+#include "samplerState.h"
 
 class PNMImage;
 class PfmFile;
@@ -147,50 +148,30 @@ PUBLISHED:
     F_rgb32,
   };
 
-  enum FilterType {
-    // Mag Filter and Min Filter
-
-    // Point sample the pixel
-    FT_nearest,
-
-    // Bilinear filtering of four neighboring pixels
-    FT_linear,
-
-    // Min Filter Only
-
-    // Point sample the pixel from the nearest mipmap level
-    FT_nearest_mipmap_nearest,
-
-    // Bilinear filter the pixel from the nearest mipmap level
-    FT_linear_mipmap_nearest,
-
-    // Point sample the pixel from two mipmap levels, and linearly blend
-    FT_nearest_mipmap_linear,
-
-    // A.k.a. trilinear filtering: Bilinear filter the pixel from
-    // two mipmap levels, and linearly blend the results.
-    FT_linear_mipmap_linear,
-
-    // The OpenGL ARB_shadow extension can be thought of as a kind of filtering.
-    FT_shadow,
-
-    // Default is usually linear, but it depends on format.
-    // This was added at the end of the list to avoid bumping TXO version #.
-    FT_default,
-
-    // Returned by string_filter_type() for an invalid match.
-    FT_invalid
+  // Deprecated.  See SamplerState.FilterType.
+  enum DeprecatedFilterType {
+    FT_nearest = SamplerState::FT_nearest,
+    FT_linear = SamplerState::FT_linear,
+    FT_nearest_mipmap_nearest = SamplerState::FT_nearest_mipmap_nearest,
+    FT_linear_mipmap_nearest = SamplerState::FT_linear_mipmap_nearest,
+    FT_nearest_mipmap_linear = SamplerState::FT_nearest_mipmap_linear,
+    FT_linear_mipmap_linear = SamplerState::FT_linear_mipmap_linear,
+    FT_shadow = SamplerState::FT_shadow,
+    FT_default = SamplerState::FT_default,
+    FT_invalid = SamplerState::FT_invalid
   };
-
-  enum WrapMode {
-    WM_clamp,  // coords that would be outside [0-1] are clamped to 0 or 1
-    WM_repeat,
-    WM_mirror,
-    WM_mirror_once,   // mirror once, then clamp
-    WM_border_color,  // coords outside [0-1] use explict border color
-    // Returned by string_wrap_mode() for an invalid match.
-    WM_invalid
+  typedef SamplerState::FilterType FilterType;
+
+  // Deprecated.  See SamplerState.WrapMode.
+  enum DeprecatedWrapMode {
+    WM_clamp = SamplerState::WM_clamp,
+    WM_repeat = SamplerState::WM_repeat,
+    WM_mirror = SamplerState::WM_mirror,
+    WM_mirror_once = SamplerState::WM_mirror_once,
+    WM_border_color = SamplerState::WM_border_color,
+    WM_invalid = SamplerState::WM_invalid
   };
+  typedef SamplerState::WrapMode WrapMode;
 
   enum CompressionMode {
     // Generic compression modes.  Usually, you should choose one of
@@ -257,7 +238,7 @@ PUBLISHED:
   BLOCKING bool read(const Filename &fullpath, const Filename &alpha_fullpath,
                      int primary_file_num_channels, int alpha_file_channel,
                      const LoaderOptions &options = LoaderOptions());
-  BLOCKING bool read(const Filename &fullpath, int z, int n, 
+  BLOCKING bool read(const Filename &fullpath, int z, int n,
                      bool read_pages, bool read_mipmaps,
                      const LoaderOptions &options = LoaderOptions());
   BLOCKING bool read(const Filename &fullpath, const Filename &alpha_fullpath,
@@ -267,7 +248,7 @@ PUBLISHED:
                      const LoaderOptions &options = LoaderOptions());
 
   BLOCKING INLINE bool write(const Filename &fullpath);
-  BLOCKING INLINE bool write(const Filename &fullpath, int z, int n, 
+  BLOCKING INLINE bool write(const Filename &fullpath, int z, int n,
                              bool write_pages, bool write_mipmaps);
 
   BLOCKING bool read_txo(istream &in, const string &filename = "");
@@ -319,13 +300,14 @@ PUBLISHED:
   INLINE void set_compression(CompressionMode compression);
   INLINE void set_render_to_texture(bool render_to_texture);
 
-  INLINE WrapMode get_wrap_u() const;
-  INLINE WrapMode get_wrap_v() const;
-  INLINE WrapMode get_wrap_w() const;
-  INLINE FilterType get_minfilter() const;
-  INLINE FilterType get_magfilter() const;
-  FilterType get_effective_minfilter() const;
-  FilterType get_effective_magfilter() const;
+  INLINE const SamplerState &get_default_sampler() const;
+  INLINE SamplerState::WrapMode get_wrap_u() const;
+  INLINE SamplerState::WrapMode get_wrap_v() const;
+  INLINE SamplerState::WrapMode get_wrap_w() const;
+  INLINE SamplerState::FilterType get_minfilter() const;
+  INLINE SamplerState::FilterType get_magfilter() const;
+  INLINE SamplerState::FilterType get_effective_minfilter() const;
+  INLINE SamplerState::FilterType get_effective_magfilter() const;
   INLINE int get_anisotropic_degree() const;
   INLINE int get_effective_anisotropic_degree() const;
   INLINE LColor get_border_color() const;
@@ -458,7 +440,7 @@ PUBLISHED:
   INLINE int get_pad_y_size() const;
   INLINE int get_pad_z_size() const;
   INLINE LVecBase2 get_tex_scale() const;
-  
+
   INLINE void set_pad_size(int x=0, int y=0, int z=0);
   void set_size_padded(int x=1, int y=1, int z=1);
 
@@ -467,7 +449,7 @@ PUBLISHED:
   INLINE int get_orig_file_z_size() const;
 
   void set_orig_file_size(int x, int y, int z = 1);
-  
+
   INLINE void set_format(Format format);
   INLINE void set_component_type(ComponentType component_type);
   INLINE void set_loaded_from_image();
@@ -503,22 +485,16 @@ PUBLISHED:
 
   static string format_format(Format f);
   static Format string_format(const string &str);
-  
-  static string format_filter_type(FilterType ft);
-  static FilterType string_filter_type(const string &str);
-  
-  static string format_wrap_mode(WrapMode wm);
-  static WrapMode string_wrap_mode(const string &str);
-  
+
   static string format_compression_mode(CompressionMode cm);
   static CompressionMode string_compression_mode(const string &str);
 
   static string format_quality_level(QualityLevel tql);
   static QualityLevel string_quality_level(const string &str);
-    
+
 public:
   void texture_uploaded();
-  
+
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const;
 
@@ -550,8 +526,8 @@ protected:
   // CData pointer representing that lock); generally, they also avoid
   // adjusting the _properties_modified and _image_modified
   // semaphores.
-  virtual bool do_adjust_this_size(const CData *cdata, 
-                                   int &x_size, int &y_size, const string &name, 
+  virtual bool do_adjust_this_size(const CData *cdata,
+                                   int &x_size, int &y_size, const string &name,
                                    bool for_padding) const;
 
   virtual bool do_read(CData *cdata,
@@ -577,7 +553,7 @@ protected:
   bool do_read_dds_file(CData *cdata, const Filename &fullpath, bool header_only);
   bool do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only);
 
-  bool do_write(CData *cdata, const Filename &fullpath, int z, int n, 
+  bool do_write(CData *cdata, const Filename &fullpath, int z, int n,
                 bool write_pages, bool write_mipmaps);
   bool do_write_one(CData *cdata, const Filename &fullpath, int z, int n);
   bool do_store_one(CData *cdata, PNMImage &pnmimage, int z, int n);
@@ -590,14 +566,14 @@ protected:
 
   PTA_uchar do_modify_ram_image(CData *cdata);
   PTA_uchar do_make_ram_image(CData *cdata);
-  void do_set_ram_image(CData *cdata, CPTA_uchar image, 
+  void do_set_ram_image(CData *cdata, CPTA_uchar image,
                         CompressionMode compression = CM_off, size_t page_size = 0);
   PTA_uchar do_modify_ram_mipmap_image(CData *cdata, int n);
   PTA_uchar do_make_ram_mipmap_image(CData *cdata, int n);
   void do_set_ram_mipmap_image(CData *cdata, int n, CPTA_uchar image, size_t page_size);
 
   bool consider_auto_process_ram_image(bool generate_mipmaps, bool allow_compression);
-  bool do_consider_auto_process_ram_image(CData *cdata, bool generate_mipmaps, 
+  bool do_consider_auto_process_ram_image(CData *cdata, bool generate_mipmaps,
                                           bool allow_compression);
   bool do_compress_ram_image(CData *cdata, CompressionMode compression,
                              QualityLevel quality_level,
@@ -607,7 +583,7 @@ protected:
 
   bool do_reconsider_z_size(CData *cdata, int z, const LoaderOptions &options);
   virtual void do_allocate_pages(CData *cdata);
-  bool do_reconsider_image_properties(CData *cdata, 
+  bool do_reconsider_image_properties(CData *cdata,
                                       int x_size, int y_size, int num_components,
                                       ComponentType component_type, int z,
                                       const LoaderOptions &options);
@@ -617,7 +593,7 @@ protected:
   PT(Texture) do_make_copy(const CData *cdata) const;
   void do_assign(CData *cdata, const Texture *copy, const CData *cdata_copy);
   virtual void do_clear(CData *cdata);
-  void do_setup_texture(CData *cdata, 
+  void do_setup_texture(CData *cdata,
                         TextureType texture_type, int x_size, int y_size,
                         int z_size, ComponentType component_type,
                         Format format);
@@ -700,28 +676,28 @@ private:
                              int num_components, int component_width,
                              CPTA_uchar image, size_t page_size,
                              int z);
-  static PTA_uchar read_dds_level_bgr8(Texture *tex, CData *cdata, const DDSHeader &header, 
+  static PTA_uchar read_dds_level_bgr8(Texture *tex, CData *cdata, const DDSHeader &header,
                                        int n, istream &in);
-  static PTA_uchar read_dds_level_rgb8(Texture *tex, CData *cdata, const DDSHeader &header, 
+  static PTA_uchar read_dds_level_rgb8(Texture *tex, CData *cdata, const DDSHeader &header,
                                        int n, istream &in);
-  static PTA_uchar read_dds_level_abgr8(Texture *tex, CData *cdata, const DDSHeader &header, 
+  static PTA_uchar read_dds_level_abgr8(Texture *tex, CData *cdata, const DDSHeader &header,
                                         int n, istream &in);
-  static PTA_uchar read_dds_level_rgba8(Texture *tex, CData *cdata, const DDSHeader &header, 
+  static PTA_uchar read_dds_level_rgba8(Texture *tex, CData *cdata, const DDSHeader &header,
                                         int n, istream &in);
-  static PTA_uchar read_dds_level_generic_uncompressed(Texture *tex, CData *cdata, 
-                                                       const DDSHeader &header, 
+  static PTA_uchar read_dds_level_generic_uncompressed(Texture *tex, CData *cdata,
+                                                       const DDSHeader &header,
                                                        int n, istream &in);
-  static PTA_uchar read_dds_level_luminance_uncompressed(Texture *tex, CData *cdata, 
-                                                         const DDSHeader &header, 
+  static PTA_uchar read_dds_level_luminance_uncompressed(Texture *tex, CData *cdata,
+                                                         const DDSHeader &header,
                                                          int n, istream &in);
-  static PTA_uchar read_dds_level_dxt1(Texture *tex, CData *cdata, 
-                                       const DDSHeader &header, 
+  static PTA_uchar read_dds_level_dxt1(Texture *tex, CData *cdata,
+                                       const DDSHeader &header,
                                        int n, istream &in);
-  static PTA_uchar read_dds_level_dxt23(Texture *tex, CData *cdata, 
-                                        const DDSHeader &header, 
+  static PTA_uchar read_dds_level_dxt23(Texture *tex, CData *cdata,
+                                        const DDSHeader &header,
                                         int n, istream &in);
-  static PTA_uchar read_dds_level_dxt45(Texture *tex, CData *cdata, 
-                                        const DDSHeader &header, 
+  static PTA_uchar read_dds_level_dxt45(Texture *tex, CData *cdata,
+                                        const DDSHeader &header,
                                         int n, istream &in);
 
   void clear_prepared(int view, PreparedGraphicsObjects *prepared_objects);
@@ -748,36 +724,36 @@ private:
                                  RamImage &to, const RamImage &from,
                                  int x_size, int y_size, int z_size) const;
 
-  typedef void Filter2DComponent(unsigned char *&p, 
+  typedef void Filter2DComponent(unsigned char *&p,
                                  const unsigned char *&q,
                                  size_t pixel_size, size_t row_size);
 
-  typedef void Filter3DComponent(unsigned char *&p, 
+  typedef void Filter3DComponent(unsigned char *&p,
                                  const unsigned char *&q,
                                  size_t pixel_size, size_t row_size,
                                  size_t page_size);
 
-  static void filter_2d_unsigned_byte(unsigned char *&p, 
+  static void filter_2d_unsigned_byte(unsigned char *&p,
                                       const unsigned char *&q,
                                       size_t pixel_size, size_t row_size);
-  static void filter_2d_unsigned_byte_srgb(unsigned char *&p, 
+  static void filter_2d_unsigned_byte_srgb(unsigned char *&p,
                                            const unsigned char *&q,
                                            size_t pixel_size, size_t row_size);
-  static void filter_2d_unsigned_short(unsigned char *&p, 
+  static void filter_2d_unsigned_short(unsigned char *&p,
                                        const unsigned char *&q,
                                        size_t pixel_size, size_t row_size);
   static void filter_2d_float(unsigned char *&p, const unsigned char *&q,
                               size_t pixel_size, size_t row_size);
 
-  static void filter_3d_unsigned_byte(unsigned char *&p, 
+  static void filter_3d_unsigned_byte(unsigned char *&p,
                                       const unsigned char *&q,
                                       size_t pixel_size, size_t row_size,
                                       size_t page_size);
-  static void filter_3d_unsigned_byte_srgb(unsigned char *&p, 
+  static void filter_3d_unsigned_byte_srgb(unsigned char *&p,
                                            const unsigned char *&q,
                                            size_t pixel_size, size_t row_size,
                                            size_t page_size);
-  static void filter_3d_unsigned_short(unsigned char *&p, 
+  static void filter_3d_unsigned_short(unsigned char *&p,
                                        const unsigned char *&q,
                                        size_t pixel_size, size_t row_size,
                                        size_t page_size);
@@ -813,16 +789,16 @@ protected:
     Filename _alpha_filename;
     Filename _fullpath;
     Filename _alpha_fullpath;
-    
+
     // The number of channels of the primary file we use.  1, 2, 3, or 4.
     int _primary_file_num_channels;
-    
+
     // If we have a separate alpha file, this designates which channel
     // in the alpha file provides the alpha channel.  0 indicates the
     // combined grayscale value of rgb; otherwise, 1, 2, 3, or 4 are
     // valid.
     int _alpha_file_channel;
-    
+
     int _x_size;
     int _y_size;
     int _z_size;
@@ -832,19 +808,14 @@ protected:
     TextureType _texture_type;
     Format _format;
     ComponentType _component_type;
-    
+
     bool _loaded_from_image;
     bool _loaded_from_txo;
     bool _has_read_pages;
     bool _has_read_mipmaps;
     int _num_mipmap_levels_read;
-    
-    WrapMode _wrap_u;
-    WrapMode _wrap_v;
-    WrapMode _wrap_w;
-    FilterType _minfilter;
-    FilterType _magfilter;
-    int _anisotropic_degree;
+
+    SamplerState _default_sampler;
     bool _keep_ram_image;
     LColor _border_color;
     CompressionMode _compression;
@@ -852,14 +823,14 @@ protected:
     bool _match_framebuffer_format;
     bool _post_load_store_cache;
     QualityLevel _quality_level;
-    
+
     int _pad_x_size;
     int _pad_y_size;
     int _pad_z_size;
-    
+
     int _orig_file_x_size;
     int _orig_file_y_size;
-  
+
     AutoTextureScale _auto_texture_scale;
     CompressionMode _ram_image_compression;
 
@@ -874,11 +845,11 @@ protected:
     int _simple_x_size;
     int _simple_y_size;
     PN_int32 _simple_image_date_generated;
-  
+
     UpdateSeq _properties_modified;
     UpdateSeq _image_modified;
     UpdateSeq _simple_image_modified;
-    
+
   public:
     static TypeHandle get_class_type() {
       return _type_handle;
@@ -886,11 +857,11 @@ protected:
     static void init_type() {
       register_type(_type_handle, "Texture::CData");
     }
-    
+
   private:
     static TypeHandle _type_handle;
   };
- 
+
   PipelineCycler<CData> _cycler;
   typedef CycleDataLockedReader<CData> CDLockedReader;
   typedef CycleDataReader<CData> CDReader;
@@ -904,7 +875,7 @@ protected:
   // Used to implement unlocked_reload_ram_image().
   ConditionVarFull _cvar;  // condition: _reloading is true.
   bool _reloading;
-    
+
   // A Texture keeps a list (actually, a map) of all the
   // PreparedGraphicsObjects tables that it has been prepared into.
   // Each PGO conversely keeps a list (a set) of all the Textures that
@@ -913,7 +884,7 @@ protected:
   typedef pmap<int, TextureContext *> Contexts;
   typedef pmap<PreparedGraphicsObjects *, Contexts> PreparedViews;
   PreparedViews _prepared_views;
-  
+
   // It is common, when using normal maps, specular maps, gloss maps,
   // and such, to use a file naming convention where the filenames
   // of the special maps are derived by concatenating a suffix to
@@ -924,7 +895,7 @@ protected:
 
   // The TexturePool finds this useful.
   Filename _texture_pool_key;
-  
+
 private:
   // The auxiliary data is not recorded to a bam file.
   typedef pmap<string, PT(TypedReferenceCount) > AuxData;
@@ -976,20 +947,11 @@ private:
 };
 
 extern EXPCL_PANDA_GOBJ ConfigVariableEnum<Texture::QualityLevel> texture_quality_level;
-extern EXPCL_PANDA_GOBJ ConfigVariableEnum<Texture::FilterType> texture_minfilter;
-extern EXPCL_PANDA_GOBJ ConfigVariableEnum<Texture::FilterType> texture_magfilter;
-extern EXPCL_PANDA_GOBJ ConfigVariableInt texture_anisotropic_degree;
 
 EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, Texture::TextureType tt);
 EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, Texture::ComponentType ct);
 EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, Texture::Format f);
 
-EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, Texture::FilterType ft);
-EXPCL_PANDA_GOBJ istream &operator >> (istream &in, Texture::FilterType &ft);
-
-EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, Texture::WrapMode wm);
-EXPCL_PANDA_GOBJ istream &operator >> (istream &in, Texture::WrapMode &wm);
-
 EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, Texture::CompressionMode cm);
 EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, Texture::QualityLevel tql);
 EXPCL_PANDA_GOBJ istream &operator >> (istream &in, Texture::QualityLevel &tql);
@@ -997,4 +959,3 @@ EXPCL_PANDA_GOBJ istream &operator >> (istream &in, Texture::QualityLevel &tql);
 #include "texture.I"
 
 #endif
-

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

@@ -19,7 +19,7 @@ TypeHandle TextureContext::_type_handle;
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureContext::output
 //       Access: Public, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 void TextureContext::
 output(ostream &out) const {
@@ -29,7 +29,7 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureContext::write
 //       Access: Published, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 void TextureContext::
 write(ostream &out, int indent_level) const {

+ 2 - 2
panda/src/grutil/multitexReducer.cxx

@@ -246,8 +246,8 @@ flatten(GraphicsOutput *window) {
 
     Texture *model_tex = model_stage._tex;
     int aniso_degree = model_tex->get_anisotropic_degree();
-    Texture::FilterType minfilter = model_tex->get_minfilter();
-    Texture::FilterType magfilter = model_tex->get_magfilter();
+    SamplerState::FilterType minfilter = model_tex->get_minfilter();
+    SamplerState::FilterType magfilter = model_tex->get_magfilter();
 
     // What is the UV range of the model stage?
     LTexCoord min_uv, max_uv;

+ 6 - 1
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -54,8 +54,10 @@ class PreparedGraphicsObjects;
 class GraphicsOutput;
 class Texture;
 class TextureContext;
-class ShaderContext;
+class SamplerContext;
+class SamplerState;
 class Shader;
+class ShaderContext;
 class RenderState;
 class TransformState;
 class Material;
@@ -148,6 +150,9 @@ public:
   virtual void release_texture(TextureContext *tc)=0;
   virtual bool extract_texture_data(Texture *tex)=0;
 
+  virtual SamplerContext *prepare_sampler(const SamplerState &sampler)=0;
+  virtual void release_sampler(SamplerContext *sc)=0;
+
   virtual GeomContext *prepare_geom(Geom *geom)=0;
   virtual void release_geom(GeomContext *gc)=0;
 

+ 2 - 2
panda/src/osxdisplay/osxGraphicsStateGuardian.cxx

@@ -135,8 +135,8 @@ draw_resize_box() {
       PT(Texture) tex = new Texture;
       tex->set_name("resize_box.rgb");
       tex->load(resize_box_pnm);
-      tex->set_minfilter(Texture::FT_linear);
-      tex->set_magfilter(Texture::FT_linear);
+      tex->set_minfilter(SamplerState::FT_linear);
+      tex->set_magfilter(SamplerState::FT_linear);
       state = state->add_attrib(TextureAttrib::make(tex));
     }
   }

+ 40 - 38
panda/src/pgraph/geomNode.cxx

@@ -141,7 +141,7 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
 
       AccumulatedAttribs geom_attribs = attribs;
       entry->_state = geom_attribs.collect(entry->_state, attrib_types);
-      
+
       bool any_changed = false;
 
       if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
@@ -152,9 +152,9 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
         }
 
         ra = entry->_state->get_attrib_def(ColorAttrib::get_class_slot());
-        CPT (ColorAttrib) ca = DCAST(ColorAttrib, ra);          
+        CPT (ColorAttrib) ca = DCAST(ColorAttrib, ra);
         if (ca->get_color_type() != ColorAttrib::T_vertex) {
-          if(allow_flatten_color) { 
+          if(allow_flatten_color) {
               if(transformer.set_color(new_geom, ca->get_color())) {
                 any_changed = true;
                 entry->_state = entry->_state->set_attrib(ColorAttrib::make_vertex());
@@ -163,15 +163,15 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
             if (transformer.remove_column(new_geom, InternalName::get_color())) {
               any_changed = true;
             }
-          }            
+          }
         }
-      }      
+      }
       if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
         if (geom_attribs._color_scale != (const RenderAttrib *)NULL) {
           CPT(ColorScaleAttrib) csa = DCAST(ColorScaleAttrib, geom_attribs._color_scale);
           if (csa->get_scale() != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) {
 
-            
+
             // Now, if we have an "off" or "flat" color attribute, we
             // simply modify the color attribute, and leave the
             // vertices alone.
@@ -187,16 +187,16 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
                 // ColorAttrib::T_off means the color scale becomes
                 // the new color.
                 entry->_state = entry->_state->set_attrib(ColorAttrib::make_flat(csa->get_scale()));
-              
+
               } else if (ca->get_color_type() == ColorAttrib::T_flat) {
                 // ColorAttrib::T_flat means the color scale modulates
                 // the specified color to produce a new color.
                 const LColor &c1 = ca->get_color();
                 const LVecBase4 &c2 = csa->get_scale();
-                LColor color(c1[0] * c2[0], c1[1] * c2[1], 
+                LColor color(c1[0] * c2[0], c1[1] * c2[1],
                              c1[2] * c2[2], c1[3] * c2[3]);
                 entry->_state = entry->_state->set_attrib(ColorAttrib::make_flat(color));
-              
+
               } else {
                 // Otherwise, we have vertex color, and we just scale
                 // it normally.
@@ -219,7 +219,7 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
           // that from which we started the flatten operation, but
           // caveat programmer.
           NameCount name_count;
-          
+
           if (geom_attribs._texture != (RenderAttrib *)NULL) {
             const TextureAttrib *ta = DCAST(TextureAttrib, geom_attribs._texture);
             int num_on_stages = ta->get_num_on_stages();
@@ -229,12 +229,12 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
               count_name(name_count, name);
             }
           }
-          
-          const TexMatrixAttrib *tma = 
+
+          const TexMatrixAttrib *tma =
             DCAST(TexMatrixAttrib, geom_attribs._tex_matrix);
-          
+
           CPT(TexMatrixAttrib) new_tma = DCAST(TexMatrixAttrib, TexMatrixAttrib::make());
-          
+
           int num_stages = tma->get_num_stages();
           for (int i = 0; i < num_stages; i++) {
             TextureStage *stage = tma->get_stage(i);
@@ -243,7 +243,7 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
               // We can't transform these texcoords, since the name is
               // used by more than one active stage.
               new_tma = DCAST(TexMatrixAttrib, new_tma->add_stage(stage, tma->get_transform(stage)));
-              
+
             } else {
               // It's safe to transform these texcoords; the name is
               // used by no more than one active stage.
@@ -252,7 +252,7 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
               }
             }
           }
-          
+
           if (!new_tma->is_empty()) {
             entry->_state = entry->_state->add_attrib(new_tma);
           }
@@ -288,7 +288,7 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
                 // invalidated our old pointer into the list, so we
                 // reassign it now.
                 entry = &(*geoms)[i];
-                
+
               } else {
                 // If there are no normals, we can just doubleside it in
                 // place.  This is preferable because we can share vertices.
@@ -297,14 +297,14 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
               }
             }
             break;
-        
+
           case CullFaceAttrib::M_cull_counter_clockwise:
             // Reverse winding order.
             new_geom->reverse_in_place();
             transformer.reverse_normals(new_geom);
             any_changed = true;
             break;
-            
+
           default:
             break;
           }
@@ -425,7 +425,7 @@ r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
     }
 
     // And now prepare each of the textures.
-    const RenderAttrib *attrib = 
+    const RenderAttrib *attrib =
       geom_state->get_attrib(TextureAttrib::get_class_slot());
     if (attrib != (const RenderAttrib *)NULL) {
       const TextureAttrib *ta;
@@ -433,6 +433,7 @@ r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
       int num_stages = ta->get_num_on_stages();
       for (int i = 0; i < num_stages; ++i) {
         Texture *texture = ta->get_on_texture(ta->get_on_stage(i));
+        //TODO: prepare the sampler states, if specified.
         if (texture != (Texture *)NULL) {
           texture->prepare(prepared_objects);
         }
@@ -448,10 +449,11 @@ r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
       if (shader != (Shader *)NULL) {
         shader->prepare(prepared_objects);
       }
+      //TODO: prepare the shader inputs.
       //TODO: Invoke the shader generator if enabled.
     }
   }
-  
+
   PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread);
 }
 
@@ -503,7 +505,7 @@ combine_with(PandaNode *other) {
 CPT(TransformState) GeomNode::
 calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
                   const TransformState *transform, Thread *current_thread) const {
-  CPT(TransformState) next_transform = 
+  CPT(TransformState) next_transform =
     PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform,
                                  current_thread);
 
@@ -552,10 +554,10 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) {
 
   if (pgraph_cat.is_spam()) {
     pgraph_cat.spam()
-      << "Found " << *this << " in state " << *data._state 
+      << "Found " << *this << " in state " << *data._state
       << " draw_mask = " << data._draw_mask << "\n";
   }
-  
+
   // Get all the Geoms, with no decalling.
   Geoms geoms = get_geoms(trav->get_current_thread());
   int num_geoms = geoms.get_num_geoms();
@@ -575,7 +577,7 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) {
       // Cull.
       continue;
     }
-    
+
     // Cull the Geom bounding volume against the view frustum
     // and/or the cull planes.  Don't bother unless we've got more
     // than one Geom, since otherwise the bounding volume of the
@@ -587,7 +589,7 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) {
         CPT(BoundingVolume) geom_volume = geom->get_bounds();
         const GeometricBoundingVolume *geom_gbv =
           DCAST(GeometricBoundingVolume, geom_volume);
-        
+
         int result = data._view_frustum->contains(geom_gbv);
         if (result == BoundingVolume::IF_no_intersection) {
           // Cull this Geom.
@@ -607,9 +609,9 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) {
         }
       }
     }
-    
-    CullableObject *object = 
-      new CullableObject(geom, state, net_transform, 
+
+    CullableObject *object =
+      new CullableObject(geom, state, net_transform,
                          modelview_transform, internal_transform);
     trav->get_cull_handler()->record_object(object, trav);
   }
@@ -803,7 +805,7 @@ unify(int max_indices, bool preserve_order) {
     CPT(GeomList) old_geoms = cdata->get_geoms();
     for (gi = old_geoms->begin(); gi != old_geoms->end(); ++gi) {
       const GeomEntry &old_entry = (*gi);
-      
+
       bool unified = false;
 
       // Go from back to front, to minimize damage to the primitive ordering.
@@ -827,14 +829,14 @@ unify(int max_indices, bool preserve_order) {
           break;
         }
       }
-      
+
       if (!unified) {
         // Couldn't unify this Geom with anything, so just add it to the
         // output list.
         new_geoms->push_back(old_entry);
       }
     }
-    
+
     // Done!  We'll keep whatever's left in the output list.
     cdata->set_geoms(new_geoms);
 
@@ -868,7 +870,7 @@ write_geoms(ostream &out, int indent_level) const {
   CPT(GeomList) geoms = cdata->get_geoms();
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
-    indent(out, indent_level + 2) 
+    indent(out, indent_level + 2)
       << *entry._geom.get_read_pointer() << " " << *entry._state << "\n";
   }
 }
@@ -888,7 +890,7 @@ write_verbose(ostream &out, int indent_level) const {
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
     CPT(Geom) geom = entry._geom.get_read_pointer();
-    indent(out, indent_level + 2) 
+    indent(out, indent_level + 2)
       << *geom << " " << *entry._state << "\n";
     geom->write(out, indent_level + 4);
   }
@@ -897,7 +899,7 @@ write_verbose(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::output
 //       Access: Public, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 void GeomNode::
 output(ostream &out) const {
@@ -923,7 +925,7 @@ output(ostream &out) const {
   out << " (" << geoms->size() << " geoms";
 
   if (!common->is_empty()) {
-    out << ": " << *common;    
+    out << ": " << *common;
   }
 
   out << ")";
@@ -1061,7 +1063,7 @@ compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
     const BoundingVolume **child_end = child_begin + child_volumes.size();
     ((BoundingVolume *)gbv)->around(child_begin, child_end);
   }
-  
+
   internal_bounds = gbv;
   internal_vertices = num_vertices;
 }
@@ -1216,7 +1218,7 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
   int num_geoms = geoms->size();
   nassertv(num_geoms == (int)(PN_uint16)num_geoms);
   dg.add_uint16(num_geoms);
-  
+
   GeomList::const_iterator gi;
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);

文件差異過大導致無法顯示
+ 228 - 118
panda/src/pgraph/nodePath.cxx


+ 20 - 14
panda/src/pgraph/nodePath.h

@@ -54,6 +54,7 @@ class MaterialCollection;
 class Fog;
 class GlobPattern;
 class PreparedGraphicsObjects;
+class SamplerState;
 class Shader;
 class ShaderInput;
 
@@ -539,7 +540,7 @@ PUBLISHED:
   INLINE void compose_color_scale(PN_stdfloat sx, PN_stdfloat sy, PN_stdfloat sz, PN_stdfloat sa,
                                   int priority = 0);
   void set_color_scale_off(int priority = 0);
-  
+
   void set_alpha_scale(PN_stdfloat scale, int priority = 0);
   void set_all_color_scale(PN_stdfloat scale, int priority = 0);
   INLINE void set_sr(PN_stdfloat sr);
@@ -573,12 +574,12 @@ PUBLISHED:
 
   void set_scissor(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top);
   void set_scissor(const LPoint3 &a, const LPoint3 &b);
-  void set_scissor(const LPoint3 &a, const LPoint3 &b, 
+  void set_scissor(const LPoint3 &a, const LPoint3 &b,
                    const LPoint3 &c, const LPoint3 &d);
-  void set_scissor(const NodePath &other, 
+  void set_scissor(const NodePath &other,
                    const LPoint3 &a, const LPoint3 &b);
   void set_scissor(const NodePath &other,
-                   const LPoint3 &a, const LPoint3 &b, 
+                   const LPoint3 &a, const LPoint3 &b,
                    const LPoint3 &c, const LPoint3 &d);
   void clear_scissor();
   bool has_scissor() const;
@@ -596,6 +597,8 @@ PUBLISHED:
 
   void set_texture(Texture *tex, int priority = 0);
   void set_texture(TextureStage *stage, Texture *tex, int priority = 0);
+  void set_texture(Texture *tex, const SamplerState &sampler, int priority = 0);
+  void set_texture(TextureStage *stage, Texture *tex, const SamplerState &sampler, int priority = 0);
   void set_texture_off(int priority = 0);
   void set_texture_off(TextureStage *stage, int priority = 0);
   void clear_texture();
@@ -606,6 +609,8 @@ PUBLISHED:
   bool has_texture_off(TextureStage *stage) const;
   Texture *get_texture() const;
   Texture *get_texture(TextureStage *stage) const;
+  const SamplerState &get_texture_sampler() const;
+  const SamplerState &get_texture_sampler(TextureStage *stage) const;
 
   void set_shader(const Shader *sha, int priority = 0);
   void set_shader_off(int priority = 0);
@@ -615,6 +620,7 @@ PUBLISHED:
 
   void set_shader_input(const ShaderInput *inp);
   void set_shader_input(const InternalName *id, Texture *tex, int priority=0);
+  void set_shader_input(const InternalName *id, Texture *tex, const SamplerState &sampler, int priority=0);
   void set_shader_input(const InternalName *id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
   void set_shader_input(const InternalName *id, const NodePath &np, int priority=0);
   void set_shader_input(const InternalName *id, const PTA_float &v, int priority=0);
@@ -698,10 +704,10 @@ PUBLISHED:
   INLINE LVecBase3 get_tex_scale_3d(const NodePath &other, TextureStage *stage) const;
 
   void set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, int priority = 0);
-  void set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, 
-                   const string &source_name, const NodePath &light, 
+  void set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode,
+                   const string &source_name, const NodePath &light,
                    int priority = 0);
-  void set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, 
+  void set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode,
                    const LTexCoord3 &constant_value,
                    int priority = 0);
   void clear_tex_gen();
@@ -915,20 +921,20 @@ PUBLISHED:
 private:
   static NodePathComponent *
   find_common_ancestor(const NodePath &a, const NodePath &b,
-                       int &a_count, int &b_count, 
+                       int &a_count, int &b_count,
                        Thread *current_thread);
 
-  CPT(RenderState) r_get_net_state(NodePathComponent *comp, 
+  CPT(RenderState) r_get_net_state(NodePathComponent *comp,
                                    Thread *current_thread) const;
   CPT(RenderState) r_get_partial_state(NodePathComponent *comp, int n,
                                        Thread *current_thread) const;
-  CPT(TransformState) r_get_net_transform(NodePathComponent *comp, 
+  CPT(TransformState) r_get_net_transform(NodePathComponent *comp,
                                           Thread *current_thread) const;
   CPT(TransformState) r_get_partial_transform(NodePathComponent *comp, int n,
                                               Thread *current_thread) const;
   CPT(TransformState) r_get_net_prev_transform(NodePathComponent *comp,
                                                Thread *current_thread) const;
-  CPT(TransformState) r_get_partial_prev_transform(NodePathComponent *comp, 
+  CPT(TransformState) r_get_partial_prev_transform(NodePathComponent *comp,
                                                    int n, Thread *current_thread) const;
 
   void find_matches(NodePathCollection &result,
@@ -937,7 +943,7 @@ private:
   void find_matches(NodePathCollection &result,
                     FindApproxPath &approx_path,
                     int max_matches) const;
-  void find_matches(NodePathCollection &result, 
+  void find_matches(NodePathCollection &result,
                     FindApproxLevelEntry *level,
                     int max_matches) const;
 
@@ -946,13 +952,13 @@ private:
 
   void r_force_recompute_bounds(PandaNode *node);
 
-  void r_set_collide_mask(PandaNode *node, 
+  void r_set_collide_mask(PandaNode *node,
                           CollideMask and_mask, CollideMask or_mask,
                           TypeHandle node_type);
 
   typedef phash_set<InternalName *, pointer_hash> InternalNames;
   bool r_has_vertex_column(PandaNode *node, const InternalName *name) const;
-  void r_find_all_vertex_columns(PandaNode *node, 
+  void r_find_all_vertex_columns(PandaNode *node,
                                  InternalNames &vertex_columns) const;
 
   typedef phash_set<Texture *, pointer_hash> Textures;

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

@@ -257,39 +257,39 @@ get_occluder_viz(CullTraverser *trav, CullTraverserData &data) {
       pgraph_cat.debug()
         << "Recomputing viz for " << *this << "\n";
     }
-    
+
     PT(GeomVertexData) vdata = new GeomVertexData
       (get_name(), GeomVertexFormat::get_v3n3t2(), Geom::UH_static);
-    
+
     // Compute the polygon normal from the first three vertices.
     LPlane plane(_vertices[0], _vertices[1], _vertices[2]);
     LVector3 poly_normal = plane.get_normal();
-    
+
     GeomVertexWriter vertex(vdata, InternalName::get_vertex());
     GeomVertexWriter normal(vdata, InternalName::get_normal());
     GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
     vertex.add_data3(_vertices[0]);
     normal.add_data3(poly_normal);
     texcoord.add_data2(0.0, 0.0);
-    
+
     vertex.add_data3(_vertices[1]);
     normal.add_data3(poly_normal);
     texcoord.add_data2(8.0, 0.0);
-    
+
     vertex.add_data3(_vertices[2]);
     normal.add_data3(poly_normal);
     texcoord.add_data2(8.0, 8.0);
-    
+
     vertex.add_data3(_vertices[3]);
     normal.add_data3(poly_normal);
     texcoord.add_data2(0.0, 8.0);
-    
+
     PT(GeomTriangles) triangles = new GeomTriangles(Geom::UH_static);
     triangles->add_vertices(0, 1, 2);
     triangles->close_primitive();
     triangles->add_vertices(0, 2, 3);
     triangles->close_primitive();
-    
+
     _occluder_viz = new Geom(vdata);
     _occluder_viz->add_primitive(triangles);
 
@@ -297,7 +297,7 @@ get_occluder_viz(CullTraverser *trav, CullTraverserData &data) {
     lines->add_vertices(0, 1, 2, 3);
     lines->add_vertex(0);
     lines->close_primitive();
-    
+
     _frame_viz = new Geom(vdata);
     _frame_viz->add_primitive(lines);
   }
@@ -321,8 +321,8 @@ get_occluder_viz_state(CullTraverser *trav, CullTraverserData &data) {
     PTA_uchar image;
     image.set_data("\x20\x80\x80\x20");
     _viz_tex->set_ram_image(image);
-    _viz_tex->set_minfilter(Texture::FT_nearest);
-    _viz_tex->set_magfilter(Texture::FT_nearest);
+    _viz_tex->set_minfilter(SamplerState::FT_nearest);
+    _viz_tex->set_magfilter(SamplerState::FT_nearest);
   }
 
   static CPT(RenderState) viz_state;

+ 98 - 55
panda/src/pgraph/shaderAttrib.cxx

@@ -81,7 +81,7 @@ make_default() {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 set_shader(const Shader *s, int priority) const {
@@ -96,7 +96,7 @@ set_shader(const Shader *s, int priority) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_off
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 set_shader_off(int priority) const {
@@ -117,7 +117,7 @@ set_shader_off(int priority) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_auto
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 set_shader_auto(int priority) const {
@@ -142,7 +142,7 @@ set_shader_auto(int priority) const {
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 set_shader_auto(BitMask32 shader_switch, int priority) const {
-  
+
   ShaderAttrib *result = new ShaderAttrib(*this);
   result->_shader = NULL;
   result->_shader_priority = priority;
@@ -160,7 +160,7 @@ set_shader_auto(BitMask32 shader_switch, int priority) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::clear_shader
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 clear_shader() const {
@@ -180,7 +180,7 @@ clear_shader() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_flag
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 set_flag(int flag, bool value) const {
@@ -194,11 +194,11 @@ set_flag(int flag, bool value) const {
   result->_has_flags |= bit;
   return return_new(result);
 }
-  
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::clear_flag
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 clear_flag(int flag) const {
@@ -212,7 +212,7 @@ clear_flag(int flag) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const ShaderInput *input) const {
@@ -229,9 +229,9 @@ set_shader_input(const ShaderInput *input) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const PTA_float &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -239,9 +239,9 @@ set_shader_input(const InternalName *id, const PTA_float &v, int priority) const
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const PTA_double &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -249,9 +249,9 @@ set_shader_input(const InternalName *id, const PTA_double &v, int priority) cons
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const PTA_LVecBase4 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -259,9 +259,9 @@ set_shader_input(const InternalName *id, const PTA_LVecBase4 &v, int priority) c
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const PTA_LVecBase3 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -270,9 +270,9 @@ set_shader_input(const InternalName *id, const PTA_LVecBase3 &v, int priority) c
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const PTA_LVecBase2 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -280,9 +280,9 @@ set_shader_input(const InternalName *id, const PTA_LVecBase2 &v, int priority) c
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const LVecBase4 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -290,9 +290,9 @@ set_shader_input(const InternalName *id, const LVecBase4 &v, int priority) const
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const LVecBase3 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -300,9 +300,9 @@ set_shader_input(const InternalName *id, const LVecBase3 &v, int priority) const
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const LVecBase2 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -310,9 +310,9 @@ set_shader_input(const InternalName *id, const LVecBase2 &v, int priority) const
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const PTA_LMatrix4 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -320,9 +320,9 @@ set_shader_input(const InternalName *id, const PTA_LMatrix4 &v, int priority) co
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const PTA_LMatrix3 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -330,9 +330,9 @@ set_shader_input(const InternalName *id, const PTA_LMatrix3 &v, int priority) co
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const LMatrix4 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -340,9 +340,9 @@ set_shader_input(const InternalName *id, const LMatrix4 &v, int priority) const
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) ShaderAttrib:: 
+CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const LMatrix3 &v, int priority) const {
   return set_shader_input(new ShaderInput(id,v,priority));
 }
@@ -350,7 +350,7 @@ set_shader_input(const InternalName *id, const LMatrix3 &v, int priority) const
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, Texture *tex, int priority) const {
@@ -360,7 +360,7 @@ set_shader_input(const InternalName *id, Texture *tex, int priority) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, const NodePath &np, int priority) const {
@@ -370,7 +370,7 @@ set_shader_input(const InternalName *id, const NodePath &np, int priority) const
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::set_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(const InternalName *id, double n1, double n2, double n3, double n4, int priority) const {
@@ -395,7 +395,7 @@ set_instance_count(int instance_count) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::clear_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 clear_shader_input(const InternalName *id) const {
@@ -407,7 +407,7 @@ clear_shader_input(const InternalName *id) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::clear_shader_input
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 clear_shader_input(const string &id) const {
@@ -458,7 +458,7 @@ get_shader_input(const string &id) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::get_shader_input_nodepath
 //       Access: Published
-//  Description: Returns the ShaderInput as a nodepath.  Assertion 
+//  Description: Returns the ShaderInput as a nodepath.  Assertion
 //               fails if there is none, or if it is not a nodepath.
 ////////////////////////////////////////////////////////////////////
 const NodePath &ShaderAttrib::
@@ -488,7 +488,7 @@ get_shader_input_nodepath(const InternalName *id) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::get_shader_input_vector
 //       Access: Published
-//  Description: Returns the ShaderInput as a vector.  Assertion 
+//  Description: Returns the ShaderInput as a vector.  Assertion
 //               fails if there is none, or if it is not a vector.
 ////////////////////////////////////////////////////////////////////
 const LVecBase4 &ShaderAttrib::
@@ -502,24 +502,33 @@ get_shader_input_vector(InternalName *id) const {
     return resfail;
   } else {
     const ShaderInput *p = (*i).second;
-    if (p->get_value_type() != ShaderInput::M_numeric) {
-      ostringstream strm;
-      strm << "Shader input " << id->get_name() << " is not a vector.\n";
-      nassert_raise(strm.str());
-      return resfail;
+
+    if (p->get_value_type() == ShaderInput::M_numeric) {
+      return p->get_vector();
+
+    } else if (p->get_value_type() == ShaderInput::M_param) {
+      // Temporary solution until the new param system
+      ParamValueBase *param = p->get_param();
+      if (param != NULL && param->is_of_type(ParamVecBase4::get_class_type())) {
+        return ((const ParamVecBase4 *) param)->get_value();
+      }
     }
-    return p->get_vector();
+
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not a vector.\n";
+    nassert_raise(strm.str());
+    return resfail;
   }
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::get_shader_input_ptr
 //       Access: Published
-//  Description: Returns the ShaderInput as a ShaderPtrData struct. 
-//               Assertion fails if there is none. or if it is not 
+//  Description: Returns the ShaderInput as a ShaderPtrData struct.
+//               Assertion fails if there is none. or if it is not
 //               a PTA(double/float)
 ////////////////////////////////////////////////////////////////////
-const Shader::ShaderPtrData *ShaderAttrib:: 
+const Shader::ShaderPtrData *ShaderAttrib::
 get_shader_input_ptr(const InternalName *id) const {
   Inputs::const_iterator i = _inputs.find(id);
   if (i == _inputs.end()) {
@@ -542,7 +551,7 @@ get_shader_input_ptr(const InternalName *id) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::get_shader_input_texture
 //       Access: Published
-//  Description: Returns the ShaderInput as a texture.  Assertion 
+//  Description: Returns the ShaderInput as a texture.  Assertion
 //               fails if there is none, or if it is not a texture.
 ////////////////////////////////////////////////////////////////////
 Texture *ShaderAttrib::
@@ -555,7 +564,8 @@ get_shader_input_texture(const InternalName *id) const {
     return NULL;
   } else {
     const ShaderInput *p = (*i).second;
-    if (p->get_value_type() != ShaderInput::M_texture) {
+    if (p->get_value_type() != ShaderInput::M_texture &&
+        p->get_value_type() != ShaderInput::M_texture_sampler) {
       ostringstream strm;
       strm <<  "Shader input " << id->get_name() << " is not a texture.\n";
       nassert_raise(strm.str());
@@ -565,6 +575,33 @@ get_shader_input_texture(const InternalName *id) const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderAttrib::get_shader_input_sampler
+//       Access: Published
+//  Description: Returns the ShaderInput as a sampler.  Assertion
+//               fails if there is none, or if it is not a texture.
+////////////////////////////////////////////////////////////////////
+const SamplerState &ShaderAttrib::
+get_shader_input_sampler(const InternalName *id) const {
+  Inputs::const_iterator i = _inputs.find(id);
+  if (i == _inputs.end()) {
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not present.\n";
+    nassert_raise(strm.str());
+    return SamplerState::get_default();
+  } else {
+    const ShaderInput *p = (*i).second;
+    if (p->get_value_type() != ShaderInput::M_texture &&
+        p->get_value_type() != ShaderInput::M_texture_sampler) {
+      ostringstream strm;
+      strm <<  "Shader input " << id->get_name() << " is not a texture.\n";
+      nassert_raise(strm.str());
+      return SamplerState::get_default();
+    }
+    return p->get_sampler();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::get_shader_input_matrix
 //       Access: Published
@@ -604,6 +641,12 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const {
           matrix = LCAST(PN_stdfloat, matrixd);
           return matrix;
         }
+        default: {
+          ostringstream strm;
+          strm << "Shader input " << id->get_name() << " does not contain floating-point data.\n";
+          nassert_raise(strm.str());
+          return LMatrix4::ident_mat();
+        }
       }
     }
 
@@ -618,7 +661,7 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const {
 //     Function: ShaderAttrib::get_shader
 //       Access: Published
 //  Description: Returns the shader object associated with the node.
-//               If get_override returns true, but get_shader 
+//               If get_override returns true, but get_shader
 //               returns NULL, that means that this attribute should
 //               disable the shader.
 ////////////////////////////////////////////////////////////////////
@@ -646,7 +689,7 @@ int ShaderAttrib::
 compare_to_impl(const RenderAttrib *other) const {
   const ShaderAttrib *that;
   DCAST_INTO_R(that, other, 0);
-  
+
   if (this->_shader != that->_shader) {
     return (this->_shader < that->_shader) ? -1 : 1;
   }
@@ -699,7 +742,7 @@ compare_to_impl(const RenderAttrib *other) const {
   if (i2 != that->_inputs.end()) {
     return -1;
   }
-  
+
   return 0;
 }
 
@@ -789,7 +832,7 @@ compose_impl(const RenderAttrib *other) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::get_auto_shader_attrib_impl
 //       Access: Protected, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ShaderAttrib::
 get_auto_shader_attrib_impl(const RenderState *state) const {

+ 8 - 9
panda/src/pgraph/shaderAttrib.h

@@ -33,11 +33,9 @@
 
 ////////////////////////////////////////////////////////////////////
 //       Class : ShaderAttrib
-// Description : 
+// Description :
 ////////////////////////////////////////////////////////////////////
-
 class EXPCL_PANDA_PGRAPH ShaderAttrib: public RenderAttrib {
-
 private:
   INLINE ShaderAttrib();
   INLINE ShaderAttrib(const ShaderAttrib &copy);
@@ -71,7 +69,7 @@ PUBLISHED:
   CPT(RenderAttrib) clear_shader() const;
   // Shader Inputs
   CPT(RenderAttrib) set_shader_input(const ShaderInput *inp) const;
-  
+
   // InternalName* id
   CPT(RenderAttrib) set_shader_input(const InternalName *id, Texture *tex,       int priority=0) const;
   CPT(RenderAttrib) set_shader_input(const InternalName *id, const NodePath &np, int priority=0) const;
@@ -88,7 +86,7 @@ PUBLISHED:
   CPT(RenderAttrib) set_shader_input(const InternalName *id, const LMatrix4 &v, int priority=0) const;
   CPT(RenderAttrib) set_shader_input(const InternalName *id, const LMatrix3 &v, int priority=0) const;
   CPT(RenderAttrib) set_shader_input(const InternalName *id, double n1=0, double n2=0, double n3=0, double n4=1,
-                                     int priority=0) const; 
+                                     int priority=0) const;
 
   CPT(RenderAttrib) set_instance_count(int instance_count) const;
 
@@ -97,11 +95,11 @@ PUBLISHED:
 
   CPT(RenderAttrib) clear_shader_input(const InternalName *id) const;
   CPT(RenderAttrib) clear_shader_input(const string &id) const;
-  
+
   CPT(RenderAttrib) clear_all_shader_inputs() const;
 
   INLINE bool get_flag(int flag) const;
-  
+
   const Shader *get_shader() const;
   const ShaderInput *get_shader_input(const InternalName *id) const;
   const ShaderInput *get_shader_input(const string &id) const;
@@ -109,11 +107,12 @@ PUBLISHED:
   const NodePath &get_shader_input_nodepath(const InternalName *id) const;
   const LVecBase4 &get_shader_input_vector(InternalName *id) const;
   Texture *get_shader_input_texture(const InternalName *id) const;
+  const SamplerState &get_shader_input_sampler(const InternalName *id) const;
   const Shader::ShaderPtrData *get_shader_input_ptr(const InternalName *id) const;
   const LMatrix4 &get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const;
 
   static void register_with_read_factory();
-  
+
 public:
 
 protected:
@@ -121,7 +120,7 @@ protected:
   virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
-  
+
 private:
 
   CPT(Shader) _shader;

+ 124 - 68
panda/src/pgraph/shaderInput.I

@@ -12,20 +12,20 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Destructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-~ShaderInput()
-{
+~ShaderInput() {
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, int priority) :
@@ -41,14 +41,14 @@ ShaderInput(const InternalName *name, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, Texture *tex, int priority) :
   _name(name),
   _type(M_texture),
   _priority(priority),
-  _stored_texture(tex),
+  _value(tex),
   _bind_layer(0),
   _bind_level(0),
   _access(A_read | A_write | A_layered)
@@ -58,14 +58,32 @@ ShaderInput(const InternalName *name, Texture *tex, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ShaderInput::
+ShaderInput(const InternalName *name, Texture *tex, const SamplerState &sampler, int priority) :
+  _name(name),
+  _type(M_texture_sampler),
+  _priority(priority),
+  _value(tex),
+  _sampler(sampler),
+  _bind_layer(0),
+  _bind_level(0),
+  _access(A_read | A_layered)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderInput::Constructor
+//       Access: Published
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, Texture *tex, bool read, bool write, int z, int n, int priority) :
   _name(name),
   _type(M_texture),
   _priority(priority),
-  _stored_texture(tex),
+  _value(tex),
   _bind_layer(z),
   _bind_level(n),
   _access(0)
@@ -87,7 +105,24 @@ ShaderInput(const InternalName *name, Texture *tex, bool read, bool write, int z
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ShaderInput::
+ShaderInput(const InternalName *name, ParamValueBase *param, int priority) :
+  _name(name),
+  _type(M_param),
+  _priority(priority),
+  _value(param),
+  _bind_layer(0),
+  _bind_level(0),
+  _access(A_read)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderInput::Constructor
+//       Access: Published
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const NodePath &np, int priority) :
@@ -104,10 +139,10 @@ ShaderInput(const InternalName *name, const NodePath &np, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_float &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_float &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -121,10 +156,10 @@ ShaderInput(const InternalName *name, const PTA_float &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LVecBase4f &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LVecBase4f &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -138,10 +173,10 @@ ShaderInput(const InternalName *name, const PTA_LVecBase4f &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LVecBase3f &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LVecBase3f &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -155,7 +190,7 @@ ShaderInput(const InternalName *name, const PTA_LVecBase3f &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const PTA_LVecBase2f &ptr, int priority) :
@@ -172,7 +207,7 @@ ShaderInput(const InternalName *name, const PTA_LVecBase2f &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LVecBase4f &vec, int priority) :
@@ -190,7 +225,7 @@ ShaderInput(const InternalName *name, const LVecBase4f &vec, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LVecBase3f &vec, int priority) :
@@ -208,7 +243,7 @@ ShaderInput(const InternalName *name, const LVecBase3f &vec, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LVecBase2f &vec, int priority) :
@@ -226,10 +261,10 @@ ShaderInput(const InternalName *name, const LVecBase2f &vec, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LMatrix4f &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LMatrix4f &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -243,10 +278,10 @@ ShaderInput(const InternalName *name, const PTA_LMatrix4f &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LMatrix3f &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LMatrix3f &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -260,7 +295,7 @@ ShaderInput(const InternalName *name, const PTA_LMatrix3f &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LMatrix4f &mat, int priority) :
@@ -277,7 +312,7 @@ ShaderInput(const InternalName *name, const LMatrix4f &mat, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LMatrix3f &mat, int priority) :
@@ -294,10 +329,10 @@ ShaderInput(const InternalName *name, const LMatrix3f &mat, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_double &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_double &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -311,10 +346,10 @@ ShaderInput(const InternalName *name, const PTA_double &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LVecBase4d &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LVecBase4d &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -328,10 +363,10 @@ ShaderInput(const InternalName *name, const PTA_LVecBase4d &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LVecBase3d &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LVecBase3d &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -345,7 +380,7 @@ ShaderInput(const InternalName *name, const PTA_LVecBase3d &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const PTA_LVecBase2d &ptr, int priority) :
@@ -362,7 +397,7 @@ ShaderInput(const InternalName *name, const PTA_LVecBase2d &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LVecBase4d &vec, int priority) :
@@ -380,7 +415,7 @@ ShaderInput(const InternalName *name, const LVecBase4d &vec, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LVecBase3d &vec, int priority) :
@@ -398,7 +433,7 @@ ShaderInput(const InternalName *name, const LVecBase3d &vec, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LVecBase2d &vec, int priority) :
@@ -416,10 +451,10 @@ ShaderInput(const InternalName *name, const LVecBase2d &vec, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LMatrix4d &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LMatrix4d &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -433,10 +468,10 @@ ShaderInput(const InternalName *name, const PTA_LMatrix4d &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LMatrix3d &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LMatrix3d &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -450,7 +485,7 @@ ShaderInput(const InternalName *name, const PTA_LMatrix3d &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LMatrix4d &mat, int priority) :
@@ -467,7 +502,7 @@ ShaderInput(const InternalName *name, const LMatrix4d &mat, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LMatrix3d &mat, int priority) :
@@ -484,10 +519,10 @@ ShaderInput(const InternalName *name, const LMatrix3d &mat, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_int &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_int &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -501,10 +536,10 @@ ShaderInput(const InternalName *name, const PTA_int &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LVecBase4i &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LVecBase4i &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -518,10 +553,10 @@ ShaderInput(const InternalName *name, const PTA_LVecBase4i &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
-ShaderInput(const InternalName *name, const PTA_LVecBase3i &ptr, int priority) : 
+ShaderInput(const InternalName *name, const PTA_LVecBase3i &ptr, int priority) :
   _name(name),
   _type(M_numeric),
   _priority(priority),
@@ -535,7 +570,7 @@ ShaderInput(const InternalName *name, const PTA_LVecBase3i &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const PTA_LVecBase2i &ptr, int priority) :
@@ -552,7 +587,7 @@ ShaderInput(const InternalName *name, const PTA_LVecBase2i &ptr, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LVecBase4i &vec, int priority) :
@@ -570,7 +605,7 @@ ShaderInput(const InternalName *name, const LVecBase4i &vec, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LVecBase3i &vec, int priority) :
@@ -588,7 +623,7 @@ ShaderInput(const InternalName *name, const LVecBase3i &vec, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ShaderInput::
 ShaderInput(const InternalName *name, const LVecBase2i &vec, int priority) :
@@ -606,7 +641,7 @@ ShaderInput(const InternalName *name, const LVecBase2i &vec, int priority) :
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::get_name
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE const InternalName *ShaderInput::
 get_name() const {
@@ -616,7 +651,7 @@ get_name() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::get_value_type
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE int ShaderInput::
 get_value_type() const {
@@ -626,7 +661,7 @@ get_value_type() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::get_priority
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE int ShaderInput::
 get_priority() const {
@@ -636,27 +671,17 @@ get_priority() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::get_texture
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE Texture *ShaderInput::
 get_texture() const {
-  return _stored_texture;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ShaderInput::get_ptr
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE const Shader::ShaderPtrData &ShaderInput::
-get_ptr() const {
-  return _stored_ptr;
+  return DCAST(Texture, _value);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::get_nodepath
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE const NodePath &ShaderInput::
 get_nodepath() const {
@@ -666,10 +691,41 @@ get_nodepath() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderInput::get_vector
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE const LVecBase4 &ShaderInput::
 get_vector() const {
   return _stored_vector;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderInput::get_ptr
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const Shader::ShaderPtrData &ShaderInput::
+get_ptr() const {
+  return _stored_ptr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderInput::get_sampler
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const SamplerState &ShaderInput::
+get_sampler() const {
+  return (_type == M_texture)
+    ? get_texture()->get_default_sampler()
+    : _sampler;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderInput::get_param
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ParamValueBase *ShaderInput::
+get_param() const {
+  return DCAST(ParamValueBase, _value);
+}

+ 21 - 12
panda/src/pgraph/shaderInput.h

@@ -20,9 +20,8 @@
 #include "typedWritableReferenceCount.h"
 #include "pointerTo.h"
 #include "nodePath.h"
-#include "texture.h"
 #include "internalName.h"
-#include "shader.h"
+#include "paramValue.h"
 #include "pta_float.h"
 #include "pta_double.h"
 #include "pta_LMatrix4.h"
@@ -30,6 +29,9 @@
 #include "pta_LVecBase4.h"
 #include "pta_LVecBase3.h"
 #include "pta_LVecBase2.h"
+#include "samplerState.h"
+#include "shader.h"
+#include "texture.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : ShaderInput
@@ -37,8 +39,7 @@
 //               one of the value types that can be passed as input
 //               to a shader.
 ////////////////////////////////////////////////////////////////////
-
-class EXPCL_PANDA_PGRAPH ShaderInput: public TypedWritableReferenceCount {
+class EXPCL_PANDA_PGRAPH ShaderInput : public TypedWritableReferenceCount {
 public:
   INLINE ~ShaderInput();
 
@@ -54,7 +55,9 @@ PUBLISHED:
   INLINE ShaderInput(const InternalName *id, int priority=0);
   INLINE ShaderInput(const InternalName *id, const NodePath &np, int priority=0);
   INLINE ShaderInput(const InternalName *id, Texture *tex, int priority=0);
+  INLINE ShaderInput(const InternalName *id, Texture *tex, const SamplerState &sampler, int priority=0);
   INLINE ShaderInput(const InternalName *id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
+  INLINE ShaderInput(const InternalName *id, ParamValueBase *param, int priority=0);
   INLINE ShaderInput(const InternalName *id, const PTA_float &ptr, int priority=0);
   INLINE ShaderInput(const InternalName *id, const PTA_LVecBase4f &ptr, int priority=0);
   INLINE ShaderInput(const InternalName *id, const PTA_LVecBase3f &ptr, int priority=0);
@@ -91,7 +94,9 @@ PUBLISHED:
     M_invalid = 0,
     M_texture,
     M_nodepath,
-    M_numeric
+    M_numeric,
+    M_texture_sampler,
+    M_param
   };
 
   INLINE const InternalName *get_name() const;
@@ -102,23 +107,27 @@ PUBLISHED:
   INLINE const NodePath &get_nodepath() const;
   INLINE const LVecBase4 &get_vector() const;
   INLINE const Shader::ShaderPtrData &get_ptr() const;
+  INLINE const SamplerState &get_sampler() const;
 
 public:
+  INLINE ParamValueBase *get_param() const;
+
   static void register_with_read_factory();
 
 private:
+  SamplerState _sampler;
+  LVecBase4 _stored_vector;
+  NodePath _stored_nodepath;
+  Shader::ShaderPtrData _stored_ptr;
   CPT(InternalName) _name;
-  int _type;
+  PT(TypedWritableReferenceCount) _value;
   int _priority;
-  Shader::ShaderPtrData _stored_ptr;
-  PT(Texture) _stored_texture;
-  NodePath _stored_nodepath;
-  LVecBase4 _stored_vector;
 
 public:
+  int _type : 8;
+  int _access : 8;
+  int _bind_level : 16;
   int _bind_layer;
-  int _bind_level;
-  int _access;
 
 public:
   static TypeHandle get_class_type() {

+ 26 - 8
panda/src/pgraph/textureAttrib.I

@@ -109,7 +109,7 @@ get_on_stage(int n) const {
 //     Function: TextureAttrib::get_num_on_ff_stages
 //       Access: Published
 //  Description: Returns the number of on-stages that are relevant
-//               to the classic fixed function pipeline.  This excludes 
+//               to the classic fixed function pipeline.  This excludes
 //               texture stages such as normal maps.
 ////////////////////////////////////////////////////////////////////
 INLINE int TextureAttrib::
@@ -123,7 +123,7 @@ get_num_on_ff_stages() const {
 //       Access: Published
 //  Description: Returns the nth stage turned on by the attribute,
 //               sorted in render order, including only those relevant
-//               to the classic fixed function pipeline.  This excludes 
+//               to the classic fixed function pipeline.  This excludes
 //               texture stages such as normal maps.
 ////////////////////////////////////////////////////////////////////
 INLINE TextureStage *TextureAttrib::
@@ -175,6 +175,24 @@ get_on_texture(TextureStage *stage) const {
   return NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureAttrib::get_on_sampler
+//       Access: Published
+//  Description: Returns the sampler associated with the indicated
+//               stage, or the one associated with its texture if
+//               no custom stage has been specified.  It is an error
+//               to call this if the stage does not exist.
+////////////////////////////////////////////////////////////////////
+INLINE const SamplerState &TextureAttrib::
+get_on_sampler(TextureStage *stage) const {
+  Stages::const_iterator si;
+  si = _on_stages.find(StageNode(stage));
+  nassertr_always(si != _on_stages.end(), SamplerState::get_default());
+
+  return si->_has_sampler ? si->_sampler
+                          : si->_texture->get_default_sampler();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureAttrib::get_on_stage_override
 //       Access: Published
@@ -269,7 +287,7 @@ check_sorted() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureAttrib::StageNode::Constructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE TextureAttrib::StageNode::
 StageNode(const TextureStage *stage, unsigned int implicit_sort, int override) :
@@ -277,11 +295,11 @@ StageNode(const TextureStage *stage, unsigned int implicit_sort, int override) :
   // deal with it properly.
   _stage((TextureStage *)stage),
   _implicit_sort(implicit_sort),
-  _override(override)
+  _override(override),
+  _has_sampler(false)
 {
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureAttrib::CompareTextureStagePriorities::operator ()
 //       Access: Public
@@ -290,7 +308,7 @@ StageNode(const TextureStage *stage, unsigned int implicit_sort, int override) :
 //               within priority, within order by sort.
 ////////////////////////////////////////////////////////////////////
 INLINE bool TextureAttrib::CompareTextureStagePriorities::
-operator () (const TextureAttrib::StageNode *a, 
+operator () (const TextureAttrib::StageNode *a,
              const TextureAttrib::StageNode *b) const {
   if (a->_stage->get_priority() != b->_stage->get_priority()) {
     return a->_stage->get_priority() > b->_stage->get_priority();
@@ -308,7 +326,7 @@ operator () (const TextureAttrib::StageNode *a,
 //               texture stages in order by sort.
 ////////////////////////////////////////////////////////////////////
 INLINE bool TextureAttrib::CompareTextureStageSort::
-operator () (const TextureAttrib::StageNode *a, 
+operator () (const TextureAttrib::StageNode *a,
              const TextureAttrib::StageNode *b) const {
   if (a->_stage->get_sort() != b->_stage->get_sort()) {
     return a->_stage->get_sort() < b->_stage->get_sort();
@@ -323,7 +341,7 @@ operator () (const TextureAttrib::StageNode *a,
 //               texture stages in order by pointer.
 ////////////////////////////////////////////////////////////////////
 INLINE bool TextureAttrib::CompareTextureStagePointer::
-operator () (const TextureAttrib::StageNode &a, 
+operator () (const TextureAttrib::StageNode &a,
              const TextureAttrib::StageNode &b) const {
   return a._stage < b._stage;
 }

+ 68 - 14
panda/src/pgraph/textureAttrib.cxx

@@ -131,6 +131,32 @@ add_on_stage(TextureStage *stage, Texture *tex, int override) const {
   (*si)._override = override;
   (*si)._texture = tex;
   (*si)._implicit_sort = attrib->_next_implicit_sort;
+  (*si)._has_sampler = false;
+  ++(attrib->_next_implicit_sort);
+
+  // We now need to re-sort the attrib list.
+  attrib->_sort_seq = UpdateSeq::old();
+  attrib->_filtered_seq = UpdateSeq::old();
+
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureAttrib::add_on_stage
+//       Access: Published
+//  Description: Returns a new TextureAttrib, just like this one, but
+//               with the indicated stage added to the list of stages
+//               turned on by this attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) TextureAttrib::
+add_on_stage(TextureStage *stage, Texture *tex, const SamplerState &sampler, int override) const {
+  TextureAttrib *attrib = new TextureAttrib(*this);
+  Stages::iterator si = attrib->_on_stages.insert(StageNode(stage)).first;
+  (*si)._override = override;
+  (*si)._texture = tex;
+  (*si)._sampler = sampler;
+  (*si)._implicit_sort = attrib->_next_implicit_sort;
+  (*si)._has_sampler = true;
   ++(attrib->_next_implicit_sort);
 
   // We now need to re-sort the attrib list.
@@ -239,7 +265,7 @@ unify_texture_stages(TextureStage *stage) const {
   for (fsi = _off_stages.begin(); fsi != _off_stages.end(); ++fsi) {
     TextureStage *this_stage = (*fsi)._stage;
 
-    if (this_stage != stage && 
+    if (this_stage != stage &&
         this_stage->get_name() == stage->get_name()) {
       this_stage = stage;
       any_changed = true;
@@ -291,7 +317,7 @@ filter_to_max(int max_texture_stages) const {
   RenderStages priority_stages = _render_stages;
 
   // This sort function uses the STL function object defined above.
-  sort(priority_stages.begin(), priority_stages.end(), 
+  sort(priority_stages.begin(), priority_stages.end(),
        CompareTextureStagePriorities());
 
   // Now lop off all of the stages after the first max_texture_stages.
@@ -362,7 +388,7 @@ lower_attrib_can_override() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureAttrib::output
 //       Access: Public, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 void TextureAttrib::
 output(ostream &out) const {
@@ -399,7 +425,7 @@ output(ostream &out) const {
       out << " on";
     }
   }
-    
+
   RenderStages::const_iterator ri;
   for (ri = _render_stages.begin(); ri != _render_stages.end(); ++ri) {
     const StageNode &sn = *(*ri);
@@ -520,6 +546,22 @@ compare_to_impl(const RenderAttrib *other) const {
       return override < other_override ? -1 : 1;
     }
 
+    int has_sampler = (*si)._has_sampler;
+    int other_has_sampler = (*osi)._has_sampler;
+
+    if (has_sampler != other_has_sampler) {
+      return has_sampler < other_has_sampler ? -1 : 1;
+    }
+
+    if (has_sampler) {
+      const SamplerState &sampler = (*si)._sampler;
+      const SamplerState &other_sampler = (*osi)._sampler;
+
+      if (sampler != other_sampler) {
+        return sampler < other_sampler ? -1 : 1;
+      }
+    }
+
     ++si;
     ++osi;
   }
@@ -560,7 +602,7 @@ compare_to_impl(const RenderAttrib *other) const {
   if (ofi != ta->_off_stages.end()) {
     return -1;
   }
-  
+
   return 0;
 }
 
@@ -641,8 +683,8 @@ compose_impl(const RenderAttrib *other) const {
   // Create a new TextureAttrib that will hold the result.
   TextureAttrib *attrib = new TextureAttrib;
 
-  while (ai != _on_stages.end() && 
-         bi != ta->_on_stages.end() && 
+  while (ai != _on_stages.end() &&
+         bi != ta->_on_stages.end() &&
          ci != ta->_off_stages.end()) {
     if ((*ai)._stage < (*bi)._stage) {
       if ((*ai)._stage < (*ci)._stage) {
@@ -717,12 +759,12 @@ compose_impl(const RenderAttrib *other) const {
       // present in the secondary.
       attrib->_on_stages.insert(attrib->_on_stages.end(), *ai);
       ++ai;
-      
+
     } else if ((*ci)._stage < (*ai)._stage) {
       // Here is a stage that is turned off in the secondary, but
       // was not present in the original.
       ++ci;
-      
+
     } else { // (*ci)._stage == (*ai)._stage
       // Here is a stage that is turned off in the secondary, and
       // was present in the original.
@@ -772,7 +814,7 @@ invert_compose_impl(const RenderAttrib *other) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureAttrib::get_auto_shader_attrib_impl
 //       Access: Protected, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) TextureAttrib::
 get_auto_shader_attrib_impl(const RenderState *state) const {
@@ -821,6 +863,10 @@ write_datagram(BamWriter *manager, Datagram &dg) {
     manager->write_pointer(dg, tex);
     dg.add_uint16((*si)._implicit_sort);
     dg.add_int32((*si)._override);
+    dg.add_bool((*si)._has_sampler);
+    if ((*si)._has_sampler) {
+      (*si)._sampler.write_datagram(dg);
+    }
   }
 }
 
@@ -850,13 +896,13 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
     // The Texture pointer filters itself through the TexturePool, so
     // we don't have to do anything special here.
     Texture *tex = DCAST(Texture, p_list[pi++]);
-    
+
     if (tex != (Texture *)NULL) {
       StageNode &sn = _on_stages[sni];
       sn._stage = ts;
       sn._texture = tex;
       ++sni;
-      
+
     } else {
       // If we couldn't load a texture pointer, turn off that
       // particular texture stage.
@@ -905,7 +951,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   // read the _off_stages data.
   _off_all_stages = scan.get_bool();
   int num_off_stages = scan.get_uint16();
-  
+
   // Push back a NULL pointer for each off TextureStage for now, until
   // we get the actual list of pointers later in complete_pointers().
   int i;
@@ -938,8 +984,16 @@ fillin(DatagramIterator &scan, BamReader *manager) {
     }
 
     _next_implicit_sort = max(_next_implicit_sort, implicit_sort + 1);
-    _on_stages.push_back(StageNode(NULL, _next_implicit_sort, override));
+    Stages::iterator si =
+      _on_stages.insert_nonunique(StageNode(NULL, _next_implicit_sort, override));
     ++_next_implicit_sort;
+
+    if (manager->get_file_minor_ver() >= 36) {
+      (*si)._has_sampler = scan.get_bool();
+      if ((*si)._has_sampler) {
+        (*si)._sampler.read_datagram(scan, manager);
+      }
+    }
   }
 }
 

+ 8 - 4
panda/src/pgraph/textureAttrib.h

@@ -61,6 +61,7 @@ PUBLISHED:
   INLINE int get_ff_tc_index(int n) const;
   INLINE bool has_on_stage(TextureStage *stage) const;
   INLINE Texture *get_on_texture(TextureStage *stage) const;
+  INLINE const SamplerState &get_on_sampler(TextureStage *stage) const;
   INLINE int get_on_stage_override(TextureStage *stage) const;
 
   int find_on_stage(const TextureStage *stage) const;
@@ -74,6 +75,8 @@ PUBLISHED:
   INLINE bool is_identity() const;
 
   CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex, int override = 0) const;
+  CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex,
+                                 const SamplerState &sampler, int override = 0) const;
   CPT(RenderAttrib) remove_on_stage(TextureStage *stage) const;
   CPT(RenderAttrib) add_off_stage(TextureStage *stage, int override = 0) const;
   CPT(RenderAttrib) remove_off_stage(TextureStage *stage) const;
@@ -102,12 +105,14 @@ private:
 private:
   class StageNode {
   public:
-    INLINE StageNode(const TextureStage *stage, 
+    INLINE StageNode(const TextureStage *stage,
                      unsigned int implicit_sort = 0,
                      int override = 0);
 
     PT(TextureStage) _stage;
     PT(Texture) _texture;
+    SamplerState _sampler;
+    bool _has_sampler;
     int _ff_tc_index;
     unsigned int _implicit_sort;
     int _override;
@@ -135,7 +140,7 @@ private:
   RenderStages _render_stages;      // all "on" stages, sorted in render order.
   RenderStages _render_ff_stages;   // fixed-function stages only, in render order.
   unsigned int _next_implicit_sort;
-  
+
   Stages _off_stages;
   bool _off_all_stages;
 
@@ -164,7 +169,7 @@ public:
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
   void fillin(DatagramIterator &scan, BamReader *manager);
-  
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;
@@ -188,4 +193,3 @@ private:
 #include "textureAttrib.I"
 
 #endif
-

+ 4 - 4
panda/src/pgraphnodes/spotlight.cxx

@@ -199,11 +199,11 @@ make_spot(int pixel_width, PN_stdfloat full_radius, LColor &fg, LColor &bg) {
   PT(Texture) tex = new Texture("spot");
   tex->load(image);
   tex->set_border_color(bg);
-  tex->set_wrap_u(Texture::WM_border_color);
-  tex->set_wrap_v(Texture::WM_border_color);
+  tex->set_wrap_u(SamplerState::WM_border_color);
+  tex->set_wrap_v(SamplerState::WM_border_color);
 
-  tex->set_minfilter(Texture::FT_linear);
-  tex->set_magfilter(Texture::FT_linear);
+  tex->set_minfilter(SamplerState::FT_linear);
+  tex->set_magfilter(SamplerState::FT_linear);
 
   return tex;
 }

+ 2 - 1
panda/src/putil/bam.h

@@ -33,7 +33,7 @@ static const unsigned short _bam_major_ver = 6;
 // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
 
 static const unsigned short _bam_first_minor_ver = 14;
-static const unsigned short _bam_minor_ver = 35;
+static const unsigned short _bam_minor_ver = 36;
 // Bumped to minor version 14 on 12/19/07 to change default ColorAttrib.
 // Bumped to minor version 15 on 4/9/08 to add TextureAttrib::_implicit_sort.
 // Bumped to minor version 16 on 5/13/08 to add Texture::_quality_level.
@@ -56,5 +56,6 @@ static const unsigned short _bam_minor_ver = 35;
 // Bumped to minor version 33 on 8/17/13 to add UvScrollNode::_w_speed.
 // Bumped to minor version 34 on 9/16/14 to add ScissorAttrib::_off.
 // Bumped to minor version 35 on 12/3/14 to change StencilAttrib.
+// Bumped to minor version 36 on 12/9/14 to add samplers and lod settings.
 
 #endif

+ 6 - 6
panda/src/rocket/rocketRenderInterface.cxx

@@ -231,8 +231,8 @@ LoadTexture(Rocket::Core::TextureHandle& texture_handle,
     return false;
   }
 
-  tex->set_minfilter(Texture::FT_nearest);
-  tex->set_magfilter(Texture::FT_nearest);
+  tex->set_minfilter(SamplerState::FT_nearest);
+  tex->set_magfilter(SamplerState::FT_nearest);
 
   texture_dimensions.x = tex->get_x_size();
   texture_dimensions.y = tex->get_y_size();
@@ -271,10 +271,10 @@ GenerateTexture(Rocket::Core::TextureHandle& texture_handle,
     }
   }
 
-  tex->set_wrap_u(Texture::WM_clamp);
-  tex->set_wrap_v(Texture::WM_clamp);
-  tex->set_minfilter(Texture::FT_nearest);
-  tex->set_magfilter(Texture::FT_nearest);
+  tex->set_wrap_u(SamplerState::WM_clamp);
+  tex->set_wrap_v(SamplerState::WM_clamp);
+  tex->set_minfilter(SamplerState::FT_nearest);
+  tex->set_magfilter(SamplerState::FT_nearest);
 
   tex->ref();
   texture_handle = (Rocket::Core::TextureHandle) tex.p();

+ 1 - 1
panda/src/testbed/test_texmem.cxx

@@ -81,7 +81,7 @@ event_T(const Event *, void *data) {
                                  (tex_y_size - white_center.get_y_size()) / 2);
       
       PT(Texture) tex = new Texture;
-      tex->set_minfilter(Texture::FT_linear_mipmap_linear);
+      tex->set_minfilter(SamplerState::FT_linear_mipmap_linear);
       tex->load(bogus_image);
 
       CardMaker cm("card");

+ 2 - 2
panda/src/testbed/text_test.cxx

@@ -54,8 +54,8 @@ void text_keys(EventHandler& eh) {
   text_node->set_card_as_margin(0.25, 0.25, 0.25, 0.25);
   PT(Texture) tex = new Texture;
   tex->set_name("genericButton.rgb");
-  tex->set_minfilter(Texture::FT_linear);
-  tex->set_magfilter(Texture::FT_linear);
+  tex->set_minfilter(SamplerState::FT_linear);
+  tex->set_magfilter(SamplerState::FT_linear);
   tex->read("/beta/toons/textures/smGreyButtonUp.rgb");
   text_node->set_card_texture( tex );
   text_node->set_card_border(0.1, 0.1);

+ 6 - 6
panda/src/text/config_text.cxx

@@ -182,14 +182,14 @@ ConfigVariableDouble text_default_underscore_height
  PRC_DESC("Specifies the default height of the underscore line, relative "
           "to the text baseline, when underscoring is enabled."));
 
-ConfigVariableEnum<Texture::FilterType> text_minfilter
-("text-minfilter", Texture::FT_linear,
+ConfigVariableEnum<SamplerState::FilterType> text_minfilter
+("text-minfilter", SamplerState::FT_linear,
  PRC_DESC("The default texture minfilter type for dynamic text fonts"));
-ConfigVariableEnum<Texture::FilterType> text_magfilter
-("text-magfilter", Texture::FT_linear,
+ConfigVariableEnum<SamplerState::FilterType> text_magfilter
+("text-magfilter", SamplerState::FT_linear,
  PRC_DESC("The default texture magfilter type for dynamic text fonts"));
-ConfigVariableEnum<Texture::WrapMode> text_wrap_mode
-("text-wrap-mode", Texture::WM_border_color,
+ConfigVariableEnum<SamplerState::WrapMode> text_wrap_mode
+("text-wrap-mode", SamplerState::WM_border_color,
  PRC_DESC("The default wrap mode for dynamic text fonts"));
 ConfigVariableEnum<Texture::QualityLevel> text_quality_level
 ("text-quality-level", Texture::QL_best,

+ 3 - 3
panda/src/text/config_text.h

@@ -50,9 +50,9 @@ extern wstring get_text_never_break_before();
 extern ConfigVariableInt text_max_never_break;
 extern ConfigVariableDouble text_default_underscore_height;
 
-extern ConfigVariableEnum<Texture::FilterType> text_minfilter;
-extern ConfigVariableEnum<Texture::FilterType> text_magfilter;
-extern ConfigVariableEnum<Texture::WrapMode> text_wrap_mode;
+extern ConfigVariableEnum<SamplerState::FilterType> text_minfilter;
+extern ConfigVariableEnum<SamplerState::FilterType> text_magfilter;
+extern ConfigVariableEnum<SamplerState::WrapMode> text_wrap_mode;
 extern ConfigVariableEnum<Texture::QualityLevel> text_quality_level;
 extern ConfigVariableEnum<TextFont::RenderMode> text_render_mode;
 

+ 4 - 4
panda/src/text/dynamicTextFont.I

@@ -293,7 +293,7 @@ get_page_y_size() const {
 //               textures created for this font.
 ////////////////////////////////////////////////////////////////////
 INLINE void DynamicTextFont::
-set_minfilter(Texture::FilterType filter) {
+set_minfilter(SamplerState::FilterType filter) {
   _minfilter = filter;
   update_filters();
 }
@@ -304,7 +304,7 @@ set_minfilter(Texture::FilterType filter) {
 //  Description: Returns the filter type used when minimizing the
 //               textures created for this font.
 ////////////////////////////////////////////////////////////////////
-INLINE Texture::FilterType DynamicTextFont::
+INLINE SamplerState::FilterType DynamicTextFont::
 get_minfilter() const {
   return _minfilter;
 }
@@ -316,7 +316,7 @@ get_minfilter() const {
 //               textures created for this font.
 ////////////////////////////////////////////////////////////////////
 INLINE void DynamicTextFont::
-set_magfilter(Texture::FilterType filter) {
+set_magfilter(SamplerState::FilterType filter) {
   _magfilter = filter;
   update_filters();
 }
@@ -327,7 +327,7 @@ set_magfilter(Texture::FilterType filter) {
 //  Description: Returns the filter type used when enlarging the
 //               textures created for this font.
 ////////////////////////////////////////////////////////////////////
-INLINE Texture::FilterType DynamicTextFont::
+INLINE SamplerState::FilterType DynamicTextFont::
 get_magfilter() const {
   return _magfilter;
 }

+ 6 - 6
panda/src/text/dynamicTextFont.h

@@ -77,10 +77,10 @@ PUBLISHED:
   INLINE int get_page_x_size() const;
   INLINE int get_page_y_size() const;
 
-  INLINE void set_minfilter(Texture::FilterType filter);
-  INLINE Texture::FilterType get_minfilter() const;
-  INLINE void set_magfilter(Texture::FilterType filter);
-  INLINE Texture::FilterType get_magfilter() const;
+  INLINE void set_minfilter(SamplerState::FilterType filter);
+  INLINE SamplerState::FilterType get_minfilter() const;
+  INLINE void set_magfilter(SamplerState::FilterType filter);
+  INLINE SamplerState::FilterType get_magfilter() const;
   INLINE void set_anisotropic_degree(int anisotropic_degree);
   INLINE int get_anisotropic_degree() const;
 
@@ -139,8 +139,8 @@ private:
   PN_stdfloat _poly_margin;
   int _page_x_size, _page_y_size;
 
-  Texture::FilterType _minfilter;
-  Texture::FilterType _magfilter;
+  SamplerState::FilterType _minfilter;
+  SamplerState::FilterType _magfilter;
   int _anisotropic_degree;
 
   RenderMode _render_mode;

+ 2 - 2
panda/src/text/staticTextFont.cxx

@@ -86,10 +86,10 @@ StaticTextFont(PandaNode *font_def, CoordinateSystem cs) {
     if (tex->get_quality_level() == Texture::QL_default) {
       tex->set_quality_level(text_quality_level);
     }
-    if (tex->get_minfilter() == Texture::FT_default) {
+    if (tex->get_minfilter() == SamplerState::FT_default) {
       tex->set_minfilter(text_minfilter);
     }
-    if (tex->get_magfilter() == Texture::FT_default) {
+    if (tex->get_magfilter() == SamplerState::FT_default) {
       tex->set_magfilter(text_magfilter);
     }
   }

+ 34 - 34
panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

@@ -2300,31 +2300,31 @@ do_issue_texture() {
     
     // Fill in the filter func pointers.  These may not actually get
     // called, if we decide below we can inline the filters.
-    Texture::FilterType minfilter = texture->get_minfilter();
-    Texture::FilterType magfilter = texture->get_magfilter();
+    SamplerState::FilterType minfilter = texture->get_minfilter();
+    SamplerState::FilterType magfilter = texture->get_magfilter();
 
     if (td_ignore_mipmaps && Texture::is_mipmap(minfilter)) {
       // Downgrade mipmaps.
-      if (minfilter == Texture::FT_nearest_mipmap_nearest) {
-        minfilter = Texture::FT_nearest;
+      if (minfilter == SamplerState::FT_nearest_mipmap_nearest) {
+        minfilter = SamplerState::FT_nearest;
       } else {
-        minfilter = Texture::FT_linear;
+        minfilter = SamplerState::FT_linear;
       }
     }
 
     // Depending on this particular texture's quality level, we may
     // downgrade the requested filters.
     if (quality_level == Texture::QL_fastest) {
-      minfilter = Texture::FT_nearest;
-      magfilter = Texture::FT_nearest;
+      minfilter = SamplerState::FT_nearest;
+      magfilter = SamplerState::FT_nearest;
 
     } else if (quality_level == Texture::QL_normal) {
       if (Texture::is_mipmap(minfilter)) {
-        minfilter = Texture::FT_nearest_mipmap_nearest;
+        minfilter = SamplerState::FT_nearest_mipmap_nearest;
       } else {
-        minfilter = Texture::FT_nearest;
+        minfilter = SamplerState::FT_nearest;
       }
-      magfilter = Texture::FT_nearest;
+      magfilter = SamplerState::FT_nearest;
 
     } else if (quality_level == Texture::QL_best) {
       minfilter = texture->get_effective_minfilter();
@@ -2334,14 +2334,14 @@ do_issue_texture() {
     texture_def->tex_minfilter_func = get_tex_filter_func(minfilter);
     texture_def->tex_magfilter_func = get_tex_filter_func(magfilter);
     
-    Texture::WrapMode wrap_u = texture->get_wrap_u();
-    Texture::WrapMode wrap_v = texture->get_wrap_v();
+    SamplerState::WrapMode wrap_u = texture->get_wrap_u();
+    SamplerState::WrapMode wrap_v = texture->get_wrap_v();
     if (td_ignore_clamp) {
-      wrap_u = Texture::WM_repeat;
-      wrap_v = Texture::WM_repeat;
+      wrap_u = SamplerState::WM_repeat;
+      wrap_v = SamplerState::WM_repeat;
     }
 
-    if (wrap_u != Texture::WM_repeat || wrap_v != Texture::WM_repeat) {
+    if (wrap_u != SamplerState::WM_repeat || wrap_v != SamplerState::WM_repeat) {
       // We have some nonstandard wrap mode.  This will force the use
       // of the general texfilter mode.
       needs_general = true;
@@ -2364,21 +2364,21 @@ do_issue_texture() {
 
       // The following special cases are handled inline, rather than
       // relying on the above wrap function pointers.
-      if (wrap_u && Texture::WM_border_color && wrap_v == Texture::WM_border_color) {
+      if (wrap_u && SamplerState::WM_border_color && wrap_v == SamplerState::WM_border_color) {
         texture_def->tex_minfilter_func = apply_wrap_border_color_minfilter;
         texture_def->tex_magfilter_func = apply_wrap_border_color_magfilter;
-      } else if (wrap_u && Texture::WM_clamp && wrap_v == Texture::WM_clamp) {
+      } else if (wrap_u && SamplerState::WM_clamp && wrap_v == SamplerState::WM_clamp) {
         texture_def->tex_minfilter_func = apply_wrap_clamp_minfilter;
         texture_def->tex_magfilter_func = apply_wrap_clamp_magfilter;
       }
     }
 
-    if (minfilter != Texture::FT_nearest || magfilter != Texture::FT_nearest) {
+    if (minfilter != SamplerState::FT_nearest || magfilter != SamplerState::FT_nearest) {
       all_nearest = false;
     }
 
-    if (minfilter != Texture::FT_nearest_mipmap_nearest ||
-        magfilter != Texture::FT_nearest) {
+    if (minfilter != SamplerState::FT_nearest_mipmap_nearest ||
+        magfilter != SamplerState::FT_nearest) {
       all_mipmap_nearest = false;
     }
 
@@ -3195,24 +3195,24 @@ get_color_blend_op(ColorBlendAttrib::Operand operand) {
 //               function according to the texture's filter type.
 ////////////////////////////////////////////////////////////////////
 ZB_lookupTextureFunc TinyGraphicsStateGuardian::
-get_tex_filter_func(Texture::FilterType filter) {
+get_tex_filter_func(SamplerState::FilterType filter) {
   switch (filter) {
-  case Texture::FT_nearest:
+  case SamplerState::FT_nearest:
     return &lookup_texture_nearest;
 
-  case Texture::FT_linear:
+  case SamplerState::FT_linear:
     return &lookup_texture_bilinear;
 
-  case Texture::FT_nearest_mipmap_nearest:
+  case SamplerState::FT_nearest_mipmap_nearest:
     return &lookup_texture_mipmap_nearest;
 
-  case Texture::FT_nearest_mipmap_linear:
+  case SamplerState::FT_nearest_mipmap_linear:
     return &lookup_texture_mipmap_linear;
       
-  case Texture::FT_linear_mipmap_nearest:
+  case SamplerState::FT_linear_mipmap_nearest:
     return &lookup_texture_mipmap_bilinear;
 
-  case Texture::FT_linear_mipmap_linear:
+  case SamplerState::FT_linear_mipmap_linear:
     return &lookup_texture_mipmap_trilinear;
 
   default:
@@ -3227,20 +3227,20 @@ get_tex_filter_func(Texture::FilterType filter) {
 //               function according to the texture's wrap mode.
 ////////////////////////////////////////////////////////////////////
 ZB_texWrapFunc TinyGraphicsStateGuardian::
-get_tex_wrap_func(Texture::WrapMode wrap_mode) {
+get_tex_wrap_func(SamplerState::WrapMode wrap_mode) {
   switch (wrap_mode) {
-  case Texture::WM_clamp:
-  case Texture::WM_border_color:  // border_color is handled later.
+  case SamplerState::WM_clamp:
+  case SamplerState::WM_border_color:  // border_color is handled later.
     return &texcoord_clamp;
 
-  case Texture::WM_repeat:
-  case Texture::WM_invalid:
+  case SamplerState::WM_repeat:
+  case SamplerState::WM_invalid:
     return &texcoord_repeat;
 
-  case Texture::WM_mirror:
+  case SamplerState::WM_mirror:
     return &texcoord_mirror;
 
-  case Texture::WM_mirror_once:
+  case SamplerState::WM_mirror_once:
     return &texcoord_mirror_once;
   }
 

+ 2 - 2
panda/src/tinydisplay/tinyGraphicsStateGuardian.h

@@ -131,8 +131,8 @@ private:
   void do_auto_rescale_normal();
   static void load_matrix(M4 *matrix, const TransformState *transform);
   static int get_color_blend_op(ColorBlendAttrib::Operand operand);
-  static ZB_lookupTextureFunc get_tex_filter_func(Texture::FilterType filter);
-  static ZB_texWrapFunc get_tex_wrap_func(Texture::WrapMode wrap_mode);
+  static ZB_lookupTextureFunc get_tex_filter_func(SamplerState::FilterType filter);
+  static ZB_texWrapFunc get_tex_wrap_func(SamplerState::WrapMode wrap_mode);
 
   INLINE void clear_light_state();
 

+ 1 - 1
pandatool/src/pfmprogs/pfmTrans.cxx

@@ -242,7 +242,7 @@ process_pfm(const Filename &input_filename, PfmFile &file) {
       if (tex == NULL) {
         nout << "Couldn't find " << _vistex_filename << "\n";
       } else {
-        tex->set_minfilter(Texture::FT_linear_mipmap_linear);
+        tex->set_minfilter(SamplerState::FT_linear_mipmap_linear);
         mesh.set_texture(tex);
         if (tex->has_alpha(tex->get_format())) {
           mesh.set_transparency(TransparencyAttrib::M_dual);

部分文件因文件數量過多而無法顯示