2
0
Эх сурвалжийг харах

Major overhaul to shader subsystem

Josh Yelon 20 жил өмнө
parent
commit
129cef9f62

+ 2 - 2
doc/makepanda/makepanda.py

@@ -2296,7 +2296,7 @@ EnqueueIgate(ipath=IPATH, opts=OPTS, outd='libpstatclient.in', obj='libpstatclie
 #
 
 IPATH=['panda/src/gobj']
-OPTS=['BUILDING_PANDA', 'NSPR']
+OPTS=['BUILDING_PANDA', 'NSPR', 'NVIDIACG']
 CopyAllHeaders('panda/src/gobj')
 EnqueueCxx(ipath=IPATH, opts=OPTS, src='gobj_composite1.cxx', obj='gobj_composite1.obj')
 EnqueueCxx(ipath=IPATH, opts=OPTS, src='gobj_composite2.cxx', obj='gobj_composite2.obj')
@@ -2572,7 +2572,7 @@ CopyAllHeaders('panda/src/physics')
 CopyAllHeaders('panda/src/particlesystem')
 IPATH=['panda/metalibs/panda']
 OPTS=['BUILDING_PANDA', 'ZLIB', 'VRPN', 'JPEG', 'PNG', 'TIFF', 'NSPR', 'FREETYPE', 'HELIX', 'FFTW', 'OPENCV',
-      'ADVAPI', 'WINSOCK2', 'WINUSER', 'WINMM']
+      'ADVAPI', 'WINSOCK2', 'WINUSER', 'WINMM', 'NVIDIACG']
 INFILES=['librecorder.in', 'libpgraph.in', 'libgrutil.in', 'libchan.in', 'libpstatclient.in',
          'libchar.in', 'libcollide.in', 'libdevice.in', 'libdgraph.in', 'libdisplay.in', 'libevent.in',
          'libgobj.in', 'libgsgbase.in', 'liblinmath.in', 'libmathutil.in', 'libparametrics.in',

+ 235 - 0
panda/src/display/graphicsStateGuardian.cxx

@@ -103,6 +103,11 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
   _current_display_region = (DisplayRegion*)0L;
   _current_stereo_channel = Lens::SC_mono;
   _current_lens = (Lens *)NULL;
+  _projection_mat = TransformState::make_identity();
+  _projection_mat_inv = TransformState::make_identity();
+  _clip_to_view = TransformState::make_identity();
+  _view_to_clip = TransformState::make_identity();
+  
   _needs_reset = true;
   _is_valid = false;
   _closing_gsg = false;
@@ -363,6 +368,15 @@ set_scene(SceneSetup *scene_setup) {
     return false;
   }
 
+  _projection_mat = calc_projection_mat(_current_lens);
+  if (_projection_mat == 0) {
+    return false;
+  }
+  _projection_mat_inv = _projection_mat->get_inverse();
+  _view_to_clip = TransformState::make_mat(_current_lens->
+       get_projection_mat(_coordinate_system, _current_stereo_channel));
+  _clip_to_view = TransformState::make_mat(_current_lens->
+       get_projection_mat_inv(_coordinate_system, _current_stereo_channel));
   return prepare_lens();
 }
 
@@ -636,6 +650,206 @@ clear(DrawableRegion *clearable) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::fetch_specified_value
+//       Access: Public
+//  Description: The gsg contains a large number of useful matrices:
+//
+//                  * the world transform,
+//                  * the modelview matrix,
+//                  * the cs_transform,
+//                  * etc, etc.
+//
+//               A shader can request any of these values, and
+//               furthermore, it can request that various compositions,
+//               inverses, and transposes be performed.  The
+//               ShaderMatSpec is a data structure indicating what
+//               datum is desired and what conversions to perform.
+//               This routine, fetch_specified_value, is responsible for
+//               doing the actual retrieval and conversions.
+//
+//               Some values, like the following, aren't matrices:
+//
+//                  * window size
+//                  * texture coordinates of card center
+//               
+//               This routine can fetch these values as well, by
+//               shoehorning them into a matrix.  In this way, we avoid
+//               the need for a separate routine to fetch these values.
+//
+//               This routine is actually a simple pcode-interpreter,
+//               and the ShaderMatSpec is actually a small pcode-array.
+//               This makes it possible to express requests for a
+//               large variety of values, and a large variety of
+//               possible conversions.
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+fetch_specified_value(const ShaderContext::ShaderMatSpec &spec, LMatrix4f &result) {
+  pvector<LMatrix4f> stack;
+  int pc = 0;
+  int ac = 0;
+  while (pc < (int)spec._opcodes.size()) {
+    int opcode = spec._opcodes[pc++];
+    switch(opcode) {
+    case ShaderContext::SMO_identity: {
+      stack.push_back(LMatrix4f::ident_mat());
+      break;
+    }
+    case ShaderContext::SMO_modelview: {
+      stack.push_back(_internal_transform->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_projection: {
+      stack.push_back(_projection_mat->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_modelproj: {
+      stack.push_back(_internal_transform->get_mat() * _projection_mat->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_window_size: {
+      stack.push_back(LMatrix4f::translate_mat(_current_display_region->get_pixel_width(),
+                                               _current_display_region->get_pixel_height(),
+                                               0.0));
+      break;
+    }
+    case ShaderContext::SMO_pixel_size: {
+      stack.push_back(LMatrix4f::translate_mat(_current_display_region->get_pixel_width(),
+                                               _current_display_region->get_pixel_height(),
+                                               0.0));
+      break;
+    }
+    case ShaderContext::SMO_card_center: {
+      int px = _current_display_region->get_pixel_width();
+      int py = _current_display_region->get_pixel_height();
+      stack.push_back(LMatrix4f::translate_mat((px*0.5) / Texture::up_to_power_2(px),
+                                               (py*0.5) / Texture::up_to_power_2(py),
+                                               0.0));
+      break;
+    }
+    case ShaderContext::SMO_mat_constant_x: {
+      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
+      if (input->get_nodepath().is_empty()) {
+        return false;
+      }
+      stack.push_back(input->get_nodepath().node()->get_transform()->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_vec_constant_x: {
+      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
+      const float *data = input->get_vector().get_data();
+      stack.push_back(LMatrix4f(data[0],data[1],data[2],data[3],
+                                data[0],data[1],data[2],data[3],
+                                data[0],data[1],data[2],data[3],
+                                data[0],data[1],data[2],data[3]));
+      break;
+    }
+    case ShaderContext::SMO_world_to_view: {
+      stack.push_back(get_scene()->get_world_transform()->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_view_to_world_C: {
+      stack.back() *= get_scene()->get_camera_transform()->get_mat();
+      break;
+    }
+    case ShaderContext::SMO_model_to_view: {
+      stack.push_back(_external_transform->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_view_to_model_C: {
+      stack.back() *= invert(_external_transform->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_apiview_to_view: {
+      stack.push_back(_inv_cs_transform->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_view_to_apiview_C: {
+      stack.back() *= _cs_transform->get_mat();
+      break;
+    }
+    case ShaderContext::SMO_clip_to_view: {
+      stack.push_back(_clip_to_view->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_view_to_clip_C: {
+      stack.back() *= _view_to_clip->get_mat();
+      break;
+    }
+    case ShaderContext::SMO_apiclip_to_view: {
+      stack.push_back(_projection_mat_inv->get_mat() * _inv_cs_transform->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_view_to_apiclip_C: {
+      stack.back() *= _cs_transform->get_mat() * _projection_mat->get_mat();
+      break;
+    }
+    case ShaderContext::SMO_view_x_to_view: {
+      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
+      nassertr(!input->get_nodepath().is_empty(), false);
+      stack.push_back(input->get_nodepath().get_net_transform()->get_mat() *
+                      get_scene()->get_world_transform()->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_view_to_view_x_C: {
+      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
+      nassertr(!input->get_nodepath().is_empty(), false);
+      stack.back()*= (get_scene()->get_camera_transform()->get_mat() *
+                      invert(input->get_nodepath().get_net_transform()->get_mat()));
+      break;
+    }
+    case ShaderContext::SMO_apiview_x_to_view: {
+      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
+      nassertr(!input->get_nodepath().is_empty(), false);
+      stack.push_back(LMatrix4f::convert_mat(_internal_coordinate_system, _coordinate_system) *
+                      input->get_nodepath().get_net_transform()->get_mat() *
+                      get_scene()->get_world_transform()->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_view_to_apiview_x_C: {
+      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
+      nassertr(!input->get_nodepath().is_empty(), false);
+      stack.back()*= (get_scene()->get_camera_transform()->get_mat() *
+                      invert(input->get_nodepath().get_net_transform()->get_mat()) *
+                      LMatrix4f::convert_mat(_coordinate_system, _internal_coordinate_system));
+      break;
+    }
+    case ShaderContext::SMO_clip_x_to_view: {
+      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
+      nassertr(!input->get_nodepath().is_empty(), false);
+      Lens *lens = DCAST(LensNode, input->get_nodepath().node())->get_lens();
+      stack.push_back(lens->get_projection_mat_inv(_coordinate_system, _current_stereo_channel) *
+                      input->get_nodepath().get_net_transform()->get_mat() *
+                      get_scene()->get_world_transform()->get_mat());
+      break;
+    }
+    case ShaderContext::SMO_view_to_clip_x_C: {
+      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
+      nassertr(!input->get_nodepath().is_empty(), false);
+      Lens *lens = DCAST(LensNode, input->get_nodepath().node())->get_lens();
+      stack.back()*= (get_scene()->get_camera_transform()->get_mat() *
+                      invert(input->get_nodepath().get_net_transform()->get_mat()) *
+                      lens->get_projection_mat(_coordinate_system, _current_stereo_channel));
+      break;
+    }
+    case ShaderContext::SMO_apiclip_x_to_view: {
+      // NOT IMPLEMENTED
+      break;
+    }
+    case ShaderContext::SMO_view_to_apiclip_x_C: {
+      // NOT IMPLEMENTED
+      break;
+    }
+
+    case ShaderContext::SMO_transpose:
+      stack.back().transpose_in_place();
+      break;
+    }
+  }
+  result = stack.back();
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::prepare_display_region
 //       Access: Public, Virtual
@@ -1832,3 +2046,24 @@ traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),vo
       return;
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::calc_projection_mat
+//       Access: Public, Virtual
+//  Description: Given a lens, this function calculates the appropriate
+//               projection matrix for this gsg.  The result depends
+//               on the peculiarities of the rendering API.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) GraphicsStateGuardian::
+calc_projection_mat(const Lens *lens) {
+  if (lens == (Lens *)NULL) {
+    return NULL;
+  }
+
+  if (!lens->is_linear()) {
+    return NULL;
+  }
+  
+  return TransformState::make_identity();
+}
+

+ 11 - 2
panda/src/display/graphicsStateGuardian.h

@@ -42,6 +42,7 @@
 #include "notify.h"
 #include "pvector.h"
 #include "attribSlots.h"
+#include "shaderContext.h"
 
 class DrawableRegion;
 class GraphicsEngine;
@@ -158,8 +159,12 @@ public:
 
   void clear(DrawableRegion *clearable);
 
+  bool fetch_specified_value(const ShaderContext::ShaderMatSpec &spec, LMatrix4f &result);
+  
   virtual void prepare_display_region(DisplayRegion *dr,
                                       Lens::StereoChannel stereo_channel);
+
+  virtual CPT(TransformState) calc_projection_mat(const Lens *lens);
   virtual bool prepare_lens();
 
   virtual bool begin_frame();
@@ -198,7 +203,7 @@ public:
 
   INLINE const TransformState *get_cs_transform() const;
   INLINE const TransformState *get_inv_cs_transform() const;
-
+  
   void do_issue_clip_plane();
   void do_issue_color();
   void do_issue_color_scale();
@@ -288,7 +293,11 @@ protected:
   CPT(DisplayRegion) _current_display_region;
   Lens::StereoChannel _current_stereo_channel;
   CPT(Lens) _current_lens;
-
+  CPT(TransformState) _projection_mat;
+  CPT(TransformState) _projection_mat_inv;
+  CPT(TransformState) _view_to_clip;
+  CPT(TransformState) _clip_to_view;
+  
   CoordinateSystem _coordinate_system;
   CoordinateSystem _internal_coordinate_system;
   CPT(TransformState) _cs_transform;

+ 40 - 40
panda/src/dxgsg9/dxShaderContext9.cxx

@@ -2058,22 +2058,22 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
   data = total->get_mat().get_data();
   switch (stb.trans_piece) {
 /*
-  case SHADER_data_matrix: cgD3D9SetMatrixParameterfc(stb.parameter, data); break;
-  case SHADER_data_transpose:  cgD3D9SetMatrixParameterfr(stb.parameter, data); break;
-  case SHADER_data_row0: cgD3D9SetParameter4fv(stb.parameter, data+ 0); break;
-  case SHADER_data_row1: cgD3D9SetParameter4fv(stb.parameter, data+ 4); break;
-  case SHADER_data_row2: cgD3D9SetParameter4fv(stb.parameter, data+ 8); break;
-  case SHADER_data_row3: cgD3D9SetParameter4fv(stb.parameter, data+12); break;
-  case SHADER_data_col0: cgD3D9SetParameter4f(stb.parameter, data[0], data[4], data[ 8], data[12]); break;
-  case SHADER_data_col1: cgD3D9SetParameter4f(stb.parameter, data[1], data[5], data[ 9], data[13]); break;
-  case SHADER_data_col2: cgD3D9SetParameter4f(stb.parameter, data[2], data[6], data[10], data[14]); break;
-  case SHADER_data_col3: cgD3D9SetParameter4f(stb.parameter, data[3], data[7], data[11], data[15]); break;
+  case SMP_whole: cgD3D9SetMatrixParameterfc(stb.parameter, data); break;
+  case SMP_transpose:  cgD3D9SetMatrixParameterfr(stb.parameter, data); break;
+  case SMP_row0: cgD3D9SetParameter4fv(stb.parameter, data+ 0); break;
+  case SMP_row1: cgD3D9SetParameter4fv(stb.parameter, data+ 4); break;
+  case SMP_row2: cgD3D9SetParameter4fv(stb.parameter, data+ 8); break;
+  case SMP_row3: cgD3D9SetParameter4fv(stb.parameter, data+12); break;
+  case SMP_col0: cgD3D9SetParameter4f(stb.parameter, data[0], data[4], data[ 8], data[12]); break;
+  case SMP_col1: cgD3D9SetParameter4f(stb.parameter, data[1], data[5], data[ 9], data[13]); break;
+  case SMP_col2: cgD3D9SetParameter4f(stb.parameter, data[2], data[6], data[10], data[14]); break;
+  case SMP_col3: cgD3D9SetParameter4f(stb.parameter, data[3], data[7], data[11], data[15]); break;
 */
 
     float vector [4];
     float matrix [16];
 
-    case SHADER_data_matrix:
+    case SMP_whole:
       if (_cg_shader) {
         cgD3D9SetUniform (stb.parameter, data);
       }
@@ -2081,7 +2081,7 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
         set_dx_shader_parameter_float (stb.dx_parameter, data, gsg -> _d3d_device);
       }
       break;
-    case SHADER_data_transpose:
+    case SMP_transpose:
       matrix [0] = data[0];
       matrix [1] = data[4];
       matrix [2] = data[8];
@@ -2105,7 +2105,7 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
         set_dx_shader_parameter_float (stb.dx_parameter, matrix, gsg -> _d3d_device);
       }
       break;
-    case SHADER_data_row0:
+    case SMP_row0:
       if (_cg_shader) {
         cgD3D9SetUniform (stb.parameter, data);
       }
@@ -2113,7 +2113,7 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
         set_dx_shader_parameter_float (stb.dx_parameter, data, gsg -> _d3d_device);
       }
       break;
-    case SHADER_data_row1:
+    case SMP_row1:
       if (_cg_shader) {
         cgD3D9SetUniform (stb.parameter, data + 4);
       }
@@ -2121,7 +2121,7 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
         set_dx_shader_parameter_float (stb.dx_parameter, data + 4, gsg -> _d3d_device);
       }
       break;
-    case SHADER_data_row2:
+    case SMP_row2:
       if (_cg_shader) {
         cgD3D9SetUniform (stb.parameter, data + 8);
       }
@@ -2129,7 +2129,7 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
         set_dx_shader_parameter_float (stb.dx_parameter, data + 8, gsg -> _d3d_device);
       }
       break;
-    case SHADER_data_row3:
+    case SMP_row3:
       if (_cg_shader) {
         cgD3D9SetUniform (stb.parameter, data + 12);
       }
@@ -2137,7 +2137,7 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
         set_dx_shader_parameter_float (stb.dx_parameter, data + 12, gsg -> _d3d_device);
       }
       break;
-    case SHADER_data_col0:
+    case SMP_col0:
       vector [0] = data[0];
       vector [1] = data[4];
       vector [2] = data[8];
@@ -2149,7 +2149,7 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
         set_dx_shader_parameter_float (stb.dx_parameter, vector, gsg -> _d3d_device);
       }
       break;
-    case SHADER_data_col1:
+    case SMP_col1:
       vector [0] = data[1];
       vector [1] = data[5];
       vector [2] = data[9];
@@ -2161,7 +2161,7 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
         set_dx_shader_parameter_float (stb.dx_parameter, vector, gsg -> _d3d_device);
       }
       break;
-    case SHADER_data_col2:
+    case SMP_col2:
       vector [0] = data[2];
       vector [1] = data[6];
       vector [2] = data[10];
@@ -2173,7 +2173,7 @@ bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
         set_dx_shader_parameter_float (stb.dx_parameter, vector, gsg -> _d3d_device);
       }
       break;
-    case SHADER_data_col3:
+    case SMP_col3:
       vector [0] = data[3];
       vector [1] = data[7];
       vector [2] = data[11];
@@ -2499,21 +2499,21 @@ compile_cg_parameter(CGparameter p, DX_PARAMETER *dx_parameter)
     bind.parameter = p;
     bind.dx_parameter = dx_parameter;
 
-    if      (pieces[0]=="trans") bind.trans_piece = SHADER_data_matrix;
-    else if (pieces[0]=="tpose") bind.trans_piece = SHADER_data_transpose;
-    else if (pieces[0]=="row0") bind.trans_piece = SHADER_data_row0;
-    else if (pieces[0]=="row1") bind.trans_piece = SHADER_data_row1;
-    else if (pieces[0]=="row2") bind.trans_piece = SHADER_data_row2;
-    else if (pieces[0]=="row3") bind.trans_piece = SHADER_data_row3;
-    else if (pieces[0]=="col0") bind.trans_piece = SHADER_data_col0;
-    else if (pieces[0]=="col1") bind.trans_piece = SHADER_data_col1;
-    else if (pieces[0]=="col2") bind.trans_piece = SHADER_data_col2;
-    else if (pieces[0]=="col3") bind.trans_piece = SHADER_data_col3;
+    if      (pieces[0]=="trans") bind.trans_piece = SMP_whole;
+    else if (pieces[0]=="tpose") bind.trans_piece = SMP_transpose;
+    else if (pieces[0]=="row0") bind.trans_piece = SMP_row0;
+    else if (pieces[0]=="row1") bind.trans_piece = SMP_row1;
+    else if (pieces[0]=="row2") bind.trans_piece = SMP_row2;
+    else if (pieces[0]=="row3") bind.trans_piece = SMP_row3;
+    else if (pieces[0]=="col0") bind.trans_piece = SMP_col0;
+    else if (pieces[0]=="col1") bind.trans_piece = SMP_col1;
+    else if (pieces[0]=="col2") bind.trans_piece = SMP_col2;
+    else if (pieces[0]=="col3") bind.trans_piece = SMP_col3;
 
     bind.src_name = InternalName::make(pieces[1]);
     bind.rel_name = InternalName::make(pieces[3]);
 
-    if ((bind.trans_piece == SHADER_data_matrix)||(bind.trans_piece == SHADER_data_transpose)) {
+    if ((bind.trans_piece == SMP_whole)||(bind.trans_piece == SMP_transpose)) {
       if (!errchk_cg_parameter_type(p, CG_FLOAT4x4)) return false;
     } else {
       if (!errchk_cg_parameter_type(p, CG_FLOAT4)) return false;
@@ -2541,18 +2541,18 @@ compile_cg_parameter(CGparameter p, DX_PARAMETER *dx_parameter)
     bind.parameter = p;
     bind.dx_parameter = dx_parameter;
 
-    if      (pieces[0]=="wstrans") { bind.rel_name = InternalName::get_world();  bind.trans_piece = SHADER_data_matrix; }
-    else if (pieces[0]=="vstrans") { bind.rel_name = InternalName::get_view();   bind.trans_piece = SHADER_data_matrix; }
-    else if (pieces[0]=="cstrans") { bind.rel_name = InternalName::get_camera(); bind.trans_piece = SHADER_data_matrix; }
-    else if (pieces[0]=="mstrans") { bind.rel_name = InternalName::get_model();  bind.trans_piece = SHADER_data_matrix; }
-    else if (pieces[0]=="wspos")   { bind.rel_name = InternalName::get_world();  bind.trans_piece = SHADER_data_row3; }
-    else if (pieces[0]=="vspos")   { bind.rel_name = InternalName::get_view();   bind.trans_piece = SHADER_data_row3; }
-    else if (pieces[0]=="cspos")   { bind.rel_name = InternalName::get_camera(); bind.trans_piece = SHADER_data_row3; }
-    else if (pieces[0]=="mspos")   { bind.rel_name = InternalName::get_model();  bind.trans_piece = SHADER_data_row3; }
+    if      (pieces[0]=="wstrans") { bind.rel_name = InternalName::get_world();  bind.trans_piece = SMP_whole; }
+    else if (pieces[0]=="vstrans") { bind.rel_name = InternalName::get_view();   bind.trans_piece = SMP_whole; }
+    else if (pieces[0]=="cstrans") { bind.rel_name = InternalName::get_camera(); bind.trans_piece = SMP_whole; }
+    else if (pieces[0]=="mstrans") { bind.rel_name = InternalName::get_model();  bind.trans_piece = SMP_whole; }
+    else if (pieces[0]=="wspos")   { bind.rel_name = InternalName::get_world();  bind.trans_piece = SMP_row3; }
+    else if (pieces[0]=="vspos")   { bind.rel_name = InternalName::get_view();   bind.trans_piece = SMP_row3; }
+    else if (pieces[0]=="cspos")   { bind.rel_name = InternalName::get_camera(); bind.trans_piece = SMP_row3; }
+    else if (pieces[0]=="mspos")   { bind.rel_name = InternalName::get_model();  bind.trans_piece = SMP_row3; }
 
     bind.src_name = InternalName::make(pieces[1]);
 
-    if ((bind.trans_piece == SHADER_data_matrix)||(bind.trans_piece == SHADER_data_transpose)) {
+    if ((bind.trans_piece == SMP_whole)||(bind.trans_piece == SMP_transpose)) {
       if (!errchk_cg_parameter_type(p, CG_FLOAT4x4)) return false;
     } else {
       if (!errchk_cg_parameter_type(p, CG_FLOAT4)) return false;

+ 39 - 27
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -845,8 +845,6 @@ reset() {
       << "max clip planes = " << _max_clip_planes << "\n";
   }
 
-  _projection_mat = LMatrix4f::ident_mat();
-
   if (_supports_multitexture) {
     GLint max_texture_stages;
     GLP(GetIntegerv)(GL_MAX_TEXTURE_UNITS, &max_texture_stages);
@@ -987,29 +985,26 @@ prepare_display_region(DisplayRegion *dr, Lens::StereoChannel stereo_channel) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GLGraphicsStateGuardian::prepare_lens
+//     Function: GLGraphicsStateGuardian::calc_projection_mat
 //       Access: Public, Virtual
-//  Description: Makes the current lens (whichever lens was most
-//               recently specified with set_scene()) active, so
-//               that it will transform future rendered geometry.
-//               Normally this is only called from the draw process,
-//               and usually it is called by set_scene().
+//  Description: Given a lens, calculates the appropriate projection
+//               matrix for use with this gsg.  Note that the
+//               projection matrix depends a lot upon the coordinate
+//               system of the rendering API.
 //
-//               The return value is true if the lens is acceptable,
-//               false if it is not.
+//               The return value is a TransformState if the lens is
+//               acceptable, NULL if it is not.
 ////////////////////////////////////////////////////////////////////
-bool CLP(GraphicsStateGuardian)::
-prepare_lens() {
-  if (_current_lens == (Lens *)NULL) {
-    return false;
+CPT(TransformState) CLP(GraphicsStateGuardian)::
+calc_projection_mat(const Lens *lens) {
+  if (lens == (Lens *)NULL) {
+    return NULL;
   }
 
-  if (!_current_lens->is_linear()) {
-    return false;
+  if (!lens->is_linear()) {
+    return NULL;
   }
 
-  const LMatrix4f &lens_mat = _current_lens->get_projection_mat(_current_stereo_channel);
-
   // The projection matrix must always be right-handed Y-up, even if
   // our coordinate system of choice is otherwise, because certain GL
   // calls (specifically glTexGen(GL_SPHERE_MAP)) assume this kind of
@@ -1017,23 +1012,40 @@ prepare_lens() {
   // other arbitrary) coordinate system, we'll use a Y-up projection
   // matrix, and store the conversion to our coordinate system of
   // choice in the modelview matrix.
-  _projection_mat =
+
+  LMatrix4f &result =
     LMatrix4f::convert_mat(CS_yup_right, _current_lens->get_coordinate_system()) *
-    lens_mat;
+    lens->get_projection_mat(_current_stereo_channel);
 
   if (_scene_setup->get_inverted()) {
     // If the scene is supposed to be inverted, then invert the
     // projection matrix.
-    static LMatrix4f invert_mat = LMatrix4f::scale_mat(1.0f, -1.0f, 1.0f);
-    _projection_mat *= invert_mat;
+    result *= LMatrix4f::scale_mat(1.0f, -1.0f, 1.0f);
   }
 
+  return TransformState::make_mat(result);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::prepare_lens
+//       Access: Public, Virtual
+//  Description: Makes the current lens (whichever lens was most
+//               recently specified with set_scene()) active, so
+//               that it will transform future rendered geometry.
+//               Normally this is only called from the draw process,
+//               and usually it is called by set_scene().
+//
+//               The return value is true if the lens is acceptable,
+//               false if it is not.
+////////////////////////////////////////////////////////////////////
+bool CLP(GraphicsStateGuardian)::
+prepare_lens() {
   if (GLCAT.is_spam()) {
     GLCAT.spam()
-      << "glMatrixMode(GL_PROJECTION): " << _projection_mat << endl;
+      << "glMatrixMode(GL_PROJECTION): " << _projection_mat->get_mat() << endl;
   }
   GLP(MatrixMode)(GL_PROJECTION);
-  GLP(LoadMatrixf)(_projection_mat.get_data());
+  GLP(LoadMatrixf)(_projection_mat->get_mat().get_data());
   report_my_gl_errors();
 
   do_point_size();
@@ -2745,7 +2757,7 @@ do_issue_transform() {
   }
 
   if (_current_shader_context)
-    _current_shader_context->issue_transform(this);
+    _current_shader_context->issue_parameters(this, false);
 
   report_my_gl_errors();
 }
@@ -2818,7 +2830,7 @@ do_issue_shader() {
     }
   } else {
     // Use the same shader as before, but with new input arguments.
-    context->issue_parameters(this);
+    context->issue_parameters(this, true);
   }
 
   report_my_gl_errors();
@@ -5965,7 +5977,7 @@ do_point_size() {
     // scaling factor based on the current viewport and projection
     // matrix.
     LVector3f height(0.0f, _point_size, 1.0f);
-    height = height * _projection_mat;
+    height = height * _projection_mat->get_mat();
     float s = height[1] * _viewport_height / _point_size;
 
     if (_current_lens->is_orthographic()) {

+ 1 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -85,6 +85,7 @@ public:
   virtual void do_clear(const RenderBuffer &buffer);
 
   virtual void prepare_display_region(DisplayRegion *dr, Lens::StereoChannel stereo_channel);
+  virtual CPT(TransformState) calc_projection_mat(const Lens *lens);
   virtual bool prepare_lens();
 
   virtual bool begin_frame();
@@ -305,7 +306,6 @@ protected:
   
   bool _dithering_enabled;
 
-  LMatrix4f _projection_mat;
   int _viewport_width;
   int _viewport_height;
   bool _auto_antialias_mode;

+ 141 - 860
panda/src/glstuff/glShaderContext_src.cxx

@@ -45,7 +45,7 @@ CLP(ShaderContext)(ShaderExpansion *s, GSG *gsg) : ShaderContext(s) {
     _cg_context = cgCreateContext();
     if (_cg_context == 0) {
       release_resources();
-      cerr << "Cg not supported by this video card.\n";
+      GLCAT.error() << "Cg not supported by this video card.\n";
       return;
     }
 
@@ -72,18 +72,17 @@ CLP(ShaderContext)(ShaderExpansion *s, GSG *gsg) : ShaderContext(s) {
     if ((_cg_profile[SHADER_type_vert] == CG_PROFILE_UNKNOWN)||
         (_cg_profile[SHADER_type_frag] == CG_PROFILE_UNKNOWN)) {
       release_resources();
-      cerr << "Cg not supported by this video card.\n";
+      GLCAT.error() << "Cg not supported by this video card.\n";
       return;
     }
 
     // Compile the program.
     try_cg_compile(s, gsg);
-    cerr << _cg_errors;
     return;
   }
 #endif
 
-  cerr << s->get_name() << ": unrecognized shader language " << header << "\n";
+  GLCAT.error() << s->get_name() << ": unrecognized shader language " << header << "\n";
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -106,7 +105,7 @@ suggest_cg_profile(const string &vpro, const string &fpro)
   _cg_profile[SHADER_type_frag] = parse_cg_profile(fpro, false);
   if ((_cg_profile[SHADER_type_vert] == CG_PROFILE_UNKNOWN)||
       (_cg_profile[SHADER_type_frag] == CG_PROFILE_UNKNOWN)) {
-    cerr << "Cg: unrecognized profile name: " << vpro << " " << fpro << "\n";
+    GLCAT.error() << "Cg: unrecognized profile name: " << vpro << " " << fpro << "\n";
     _cg_profile[SHADER_type_vert] = CG_PROFILE_UNKNOWN;
     _cg_profile[SHADER_type_frag] = CG_PROFILE_UNKNOWN;
     return;
@@ -161,19 +160,17 @@ parse_cg_profile(const string &id, bool vertex)
 bool CLP(ShaderContext)::
 try_cg_compile(ShaderExpansion *s, GSG *gsg)
 {
-  _cg_errors = "";
-
   cgGetError();
   _cg_program[0] =
     cgCreateProgram(_cg_context, CG_SOURCE, s->_text.c_str(),
                     _cg_profile[0], "vshader", (const char**)NULL);
-  print_cg_compile_errors(s->get_name(), _cg_context);
+  report_cg_compile_errors(s->get_name(), _cg_context, GLCAT.get_safe_ptr());
 
   cgGetError();
   _cg_program[1] =
     cgCreateProgram(_cg_context, CG_SOURCE, s->_text.c_str(),
                     _cg_profile[1], "fshader", (const char**)NULL);
-  print_cg_compile_errors(s->get_name(), _cg_context);
+  report_cg_compile_errors(s->get_name(), _cg_context, GLCAT.get_safe_ptr());
 
   if ((_cg_program[SHADER_type_vert]==0)||(_cg_program[SHADER_type_frag]==0)) {
     release_resources();
@@ -186,7 +183,7 @@ try_cg_compile(ShaderExpansion *s, GSG *gsg)
     const char *program;
     program = cgGetProgramString (_cg_program[1], CG_COMPILED_PROGRAM);
 
-    glgsg_cat.debug ( ) << program << "\n";
+    GLCAT.debug() << program << "\n";
   }
 #endif
 
@@ -254,7 +251,7 @@ try_cg_compile(ShaderExpansion *s, GSG *gsg)
       _cg_program[1] =
         cgCreateProgram(_cg_context, CG_OBJECT, result.c_str(),
                         _cg_profile[1], "fshader", (const char**)NULL);
-      print_cg_compile_errors(s->get_name(), _cg_context);
+      report_cg_compile_errors(s->get_name(), _cg_context, GLCAT.get_safe_ptr());
       if (_cg_program[SHADER_type_frag]==0) {
         release_resources();
         return false;
@@ -265,10 +262,15 @@ try_cg_compile(ShaderExpansion *s, GSG *gsg)
   bool success = true;
   CGparameter parameter;
   for (int progindex=0; progindex<2; progindex++) {
+    int nvtx = _var_spec.size();
     for (parameter = cgGetFirstLeafParameter(_cg_program[progindex],CG_PROGRAM);
          parameter != 0;
          parameter = cgGetNextLeafParameter(parameter)) {
-      success &= compile_cg_parameter(parameter);
+      success &= compile_cg_parameter(parameter, GLCAT.get_safe_ptr());
+    }
+    if ((progindex == SHADER_type_frag) && (nvtx != _var_spec.size())) {
+      GLCAT.error() << "Cannot use vtx parameters in an fshader\n";
+      success = false;
     }
   }
   if (!success) {
@@ -279,10 +281,6 @@ try_cg_compile(ShaderExpansion *s, GSG *gsg)
   cgGLLoadProgram(_cg_program[SHADER_type_vert]);
   cgGLLoadProgram(_cg_program[SHADER_type_frag]);
 
-  _cg_errors = s->get_name() + ": compiled to "
-    + cgGetProfileString(_cg_profile[SHADER_type_vert]) + " "
-    + cgGetProfileString(_cg_profile[SHADER_type_frag]) + "\n";
-
   _state = true;
 
   return true;
@@ -332,7 +330,7 @@ bind(GSG *gsg) {
   if (_cg_context != 0) {
 
     // Pass in k-parameters and transform-parameters
-    issue_parameters(gsg);
+    issue_parameters(gsg, true);
 
     // Bind the shaders.
     cgGLEnableProfile(_cg_profile[SHADER_type_vert]);
@@ -359,166 +357,44 @@ unbind()
 #endif
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GLShaderContext::issue_cg_auto_bind
-//       Access: Public
-//  Description: Pass a single system parameter into the shader.
-////////////////////////////////////////////////////////////////////
-#ifdef HAVE_CGGL
-void CLP(ShaderContext)::
-issue_cg_auto_bind(const ShaderAutoBind &bind, GSG *gsg)
-{
-  LVecBase4f t; float xhi,yhi; int px,py;
-
-  CGparameter p = bind.parameter;
-  switch(bind.value) {
-  case SIC_mat_modelview:
-  case SIC_inv_modelview:
-  case SIC_tps_modelview:
-  case SIC_itp_modelview:
-  case SIC_mat_projection:
-  case SIC_inv_projection:
-  case SIC_tps_projection:
-  case SIC_itp_projection:
-  case SIC_mat_texture:
-  case SIC_inv_texture:
-  case SIC_tps_texture:
-  case SIC_itp_texture:
-  case SIC_mat_modelproj:
-  case SIC_inv_modelproj:
-  case SIC_tps_modelproj:
-  case SIC_itp_modelproj:
-
-/*
-    float data [16];
-
-    glGetFloatv (GL_MODELVIEW_MATRIX, data);
-    glgsg_cat.debug ( ) << "MODELVIEW MATRIX \n" <<
-      data[ 0] << " " << data[ 1] << " " << data[ 2] << " " << data[ 3] << "\n" <<
-      data[ 4] << " " << data[ 5] << " " << data[ 6] << " " << data[ 7] << "\n" <<
-      data[ 8] << " " << data[ 9] << " " << data[10] << " " << data[11] << "\n" <<
-      data[12] << " " << data[13] << " " << data[14] << " " << data[15] << "\n";
-
-    glGetFloatv (GL_PROJECTION_MATRIX, data);
-    glgsg_cat.debug ( ) << "PROJECTION MATRIX \n" <<
-      data[ 0] << " " << data[ 1] << " " << data[ 2] << " " << data[ 3] << "\n" <<
-      data[ 4] << " " << data[ 5] << " " << data[ 6] << " " << data[ 7] << "\n" <<
-      data[ 8] << " " << data[ 9] << " " << data[10] << " " << data[11] << "\n" <<
-      data[12] << " " << data[13] << " " << data[14] << " " << data[15] << "\n";
-*/
-
-    cgGLSetStateMatrixParameter(p, (CGGLenum)((bind.value >> 2)+4), (CGGLenum)(bind.value & 3));
-    return;
-  case SIC_sys_windowsize:
-    t[0] = gsg->_current_display_region->get_pixel_width();
-    t[1] = gsg->_current_display_region->get_pixel_height();
-    t[2] = 1;
-    t[3] = 1;
-    cgGLSetParameter4fv(p, t.get_data());
-    return;
-  case SIC_sys_pixelsize:
-    t[0] = 1.0 / gsg->_current_display_region->get_pixel_width();
-    t[1] = 1.0 / gsg->_current_display_region->get_pixel_height();
-    t[2] = 1;
-    t[3] = 1;
-    cgGLSetParameter4fv(p, t.get_data());
-    return;
-  case SIC_sys_cardcenter:
-    px = gsg->_current_display_region->get_pixel_width();
-    py = gsg->_current_display_region->get_pixel_height();
-    xhi = (px*1.0) / Texture::up_to_power_2(px);
-    yhi = (py*1.0) / Texture::up_to_power_2(py);
-    t[0] = xhi*0.5;
-    t[1] = yhi*0.5;
-    t[2] = 1;
-    t[3] = 1;
-    cgGLSetParameter4fv(p, t.get_data());
-    return;
-  }
-}
-#endif
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GLShaderContext::issue_parameters
 //       Access: Public
 //  Description: This function gets called whenever the RenderState
-//               has changed, but the ShaderExpansion itself has not
-//               changed.  It loads new parameters into the
-//               already-bound shader.
+//               or TransformState has changed, but the ShaderExpansion
+//               itself has not changed.  It loads new values into the
+//               shader's parameters.  The flag "all" is false if the
+//               only thing that has changed is the modelview matrix.
+//               In this case, only the transform-dependent parameters
+//               are reloaded.
 ////////////////////////////////////////////////////////////////////
 void CLP(ShaderContext)::
-issue_parameters(GSG *gsg)
+issue_parameters(GSG *gsg, bool all)
 {
 #ifdef HAVE_CGGL
-  if (_cg_context != 0) {
-    // Pass in k-float parameters.
-    for (int i=0; i<(int)_cg_fbind.size(); i++) {
-      InternalName *id = _cg_fbind[i].name;
-      const ShaderInput *input = gsg->_target._shader->get_shader_input(id);
-      cgGLSetParameter4fv(_cg_fbind[i].parameter, input->get_vector().get_data());
-
-#if DEBUG_GL_SHADER
-      // DEBUG
-      {
-        const float *data;
-        char string [256];
-
-        data = input->get_vector().get_data();
-        sprintf (string, "%f  %f  %f  %f \n", data [0], data [1], data [2], data [3]);
-
-        glgsg_cat.debug ( ) << "SHADER: issue_parameters, _cg_fbind " << id -> get_name ( ) << "\n";
-        glgsg_cat.debug ( ) << string;
-      }
-#endif
-    }
-
-    // Pass in k-float4x4 parameters.
-    for (int i=0; i<(int)_cg_npbind.size(); i++) {
-      InternalName *id = _cg_npbind[i].name;
-      const ShaderInput *input = gsg->_target._shader->get_shader_input(id);
-      const float *dat;
-      if (input->get_nodepath().is_empty()) {
-        dat = LMatrix4f::ident_mat().get_data();
-      } else {
-        dat = input->get_nodepath().node()->get_transform()->get_mat().get_data();
-      }
-      cgGLSetMatrixParameterfc(_cg_npbind[i].parameter, dat);
-    }
-
-    // Pass in system parameters
-    for (int i=0; i<(int)_cg_auto_param.size(); i++) {
-      issue_cg_auto_bind(_cg_auto_param[i], gsg);
-    }
-
-    // Pass in trans,tpose,row,col,xvec,yvec,zvec,pos parameters
-    for (int i=0; i<(int)_cg_parameter_bind.size(); i++) {
-      bind_cg_transform(_cg_parameter_bind[i], gsg);
-    }
+  if (_cg_context == 0) {
+    return;
   }
-#endif
-  issue_transform(gsg);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GLShaderContext::issue_transform
-//       Access: Public
-//  Description: This function gets called whenever the RenderState
-//               or the TransformState has changed, but the
-//               ShaderExpansion itself has not changed.  It loads
-//               new parameters into the already-bound shader.
-////////////////////////////////////////////////////////////////////
-void CLP(ShaderContext)::
-issue_transform(GSG *gsg)
-{
-#ifdef HAVE_CGGL
-  if (_cg_context != 0) {
-    // Pass in modelview, projection, etc.
-    for (int i=0; i<(int)_cg_auto_trans.size(); i++) {
-      issue_cg_auto_bind(_cg_auto_trans[i], gsg);
-    }
-    // Pass in trans,tpose,row,col,xvec,yvec,zvec,pos parameters
-    for (int i=0; i<(int)_cg_transform_bind.size(); i++) {
-      bind_cg_transform(_cg_transform_bind[i], gsg);
+  
+  for (int i=0; i<(int)_mat_spec.size(); i++) {
+    if (all || _mat_spec[i]._trans_dependent) {
+      LMatrix4f result;
+      CGparameter p = (CGparameter)(_mat_spec[i]._parameter);
+      if (gsg->fetch_specified_value(_mat_spec[i], result)) {
+        const float *data = result.get_data();
+        switch (_mat_spec[i]._piece) {
+        case SMP_whole: cgGLSetMatrixParameterfc(p, data); break;
+        case SMP_transpose: cgGLSetMatrixParameterfr(p, data); break;
+        case SMP_row0: cgGLSetParameter4fv(p, data+ 0); break;
+        case SMP_row1: cgGLSetParameter4fv(p, data+ 4); break;
+        case SMP_row2: cgGLSetParameter4fv(p, data+ 8); break;
+        case SMP_row3: cgGLSetParameter4fv(p, data+12); break;
+        case SMP_col0: cgGLSetParameter4f(p, data[0], data[4], data[ 8], data[12]); break;
+        case SMP_col1: cgGLSetParameter4f(p, data[1], data[5], data[ 9], data[13]); break;
+        case SMP_col2: cgGLSetParameter4f(p, data[2], data[6], data[10], data[14]); break;
+        case SMP_col3: cgGLSetParameter4f(p, data[3], data[7], data[11], data[15]); break;
+        }
+      }
     }
   }
 #endif
@@ -533,9 +409,14 @@ void CLP(ShaderContext)::
 disable_shader_vertex_arrays(GSG *gsg)
 {
 #ifdef HAVE_CGGL
-  if (_cg_context)
-    for (int i=0; i<(int)_cg_varying.size(); i++)
-      cgGLDisableClientState(_cg_varying[i].parameter);
+  if (_cg_context == 0) {
+    return;
+  }
+  
+  for (int i=0; i<(int)_var_spec.size(); i++) {
+    CGparameter p = (CGparameter)(_var_spec[i]._parameter);
+    cgGLDisableClientState(p);
+  }
 #endif
 }
 
@@ -555,44 +436,47 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg)
 {
   if (prev) prev->disable_shader_vertex_arrays(gsg);
 #ifdef HAVE_CGGL
-  if (_cg_context) {
+  if (_cg_context == 0) {
+    return;
+  }
+  
 #ifdef SUPPORT_IMMEDIATE_MODE
-    if (gsg->_use_sender) {
-      cerr << "immediate mode shaders not implemented yet\n";
-    } else
+  if (gsg->_use_sender) {
+    GLCAT.error() << "immediate mode shaders not implemented yet\n";
+  } else
 #endif // SUPPORT_IMMEDIATE_MODE
-    {
-      const GeomVertexArrayData *array_data;
-      Geom::NumericType numeric_type;
-      int start, stride, num_values;
-      int nvarying = _cg_varying.size();
-      for (int i=0; i<nvarying; i++) {
-        InternalName *name = _cg_varying[i].name;
-        int texslot = _cg_varying[i].append_uv;
-        if (texslot >= 0) {
-          const Geom::ActiveTextureStages &active_stages =
-            gsg->_state._texture->get_on_stages();
-          if (texslot < (int)active_stages.size()) {
-            TextureStage *stage = active_stages[texslot];
-            InternalName *texname = stage->get_texcoord_name();
-            if (name == InternalName::get_texcoord()) {
-              name = texname;
-            } else if (texname != InternalName::get_texcoord()) {
-              name = name->append(texname->get_basename());
-            }
+  {
+    const GeomVertexArrayData *array_data;
+    Geom::NumericType numeric_type;
+    int start, stride, num_values;
+    int nvarying = _var_spec.size();
+    for (int i=0; i<nvarying; i++) {
+      CGparameter p = (CGparameter)(_var_spec[i]._parameter);
+      InternalName *name = _var_spec[i]._name;
+      int texslot = _var_spec[i]._append_uv;
+      if (texslot >= 0) {
+        const Geom::ActiveTextureStages &active_stages =
+          gsg->_state._texture->get_on_stages();
+        if (texslot < (int)active_stages.size()) {
+          TextureStage *stage = active_stages[texslot];
+          InternalName *texname = stage->get_texcoord_name();
+          if (name == InternalName::get_texcoord()) {
+            name = texname;
+          } else if (texname != InternalName::get_texcoord()) {
+            name = name->append(texname->get_basename());
           }
         }
-        if (gsg->_vertex_data->get_array_info(name,
-                                              array_data, num_values, numeric_type,
-                                              start, stride)) {
-          const unsigned char *client_pointer = gsg->setup_array_data(array_data);
-          cgGLSetParameterPointer(_cg_varying[i].parameter,
-                                  num_values, gsg->get_numeric_type(numeric_type),
-                                  stride, client_pointer + start);
-          cgGLEnableClientState(_cg_varying[i].parameter);
-        } else {
-          cgGLDisableClientState(_cg_varying[i].parameter);
-        }
+      }
+      if (gsg->_vertex_data->get_array_info(name,
+                                            array_data, num_values, numeric_type,
+                                            start, stride)) {
+        const unsigned char *client_pointer = gsg->setup_array_data(array_data);
+        cgGLSetParameterPointer(p,
+                                num_values, gsg->get_numeric_type(numeric_type),
+                                stride, client_pointer + start);
+        cgGLEnableClientState(p);
+      } else {
+        cgGLDisableClientState(p);
       }
     }
   }
@@ -608,21 +492,24 @@ void CLP(ShaderContext)::
 disable_shader_texture_bindings(GSG *gsg)
 {
 #ifdef HAVE_CGGL
-  if (_cg_context) {
-    for (int i=0; i<(int)_cg_texbind.size(); i++) {
-      int texunit = cgGetParameterResourceIndex(_cg_texbind[i].parameter);
-      gsg->_glActiveTexture(GL_TEXTURE0 + texunit);
-      GLP(Disable)(GL_TEXTURE_1D);
-      GLP(Disable)(GL_TEXTURE_2D);
-      if (gsg->_supports_3d_texture) {
-        GLP(Disable)(GL_TEXTURE_3D);
-      }
-      if (gsg->_supports_cube_map) {
-        GLP(Disable)(GL_TEXTURE_CUBE_MAP);
-      }
-      // This is probably faster - but maybe not as safe?
-      // cgGLDisableTextureParameter(_cg_texbind[i].parameter);
+  if (_cg_context == 0) {
+    return;
+  }
+
+  for (int i=0; i<(int)_tex_spec.size(); i++) {
+    CGparameter p = (CGparameter)(_tex_spec[i]._parameter);
+    int texunit = cgGetParameterResourceIndex(p);
+    gsg->_glActiveTexture(GL_TEXTURE0 + texunit);
+    GLP(Disable)(GL_TEXTURE_1D);
+    GLP(Disable)(GL_TEXTURE_2D);
+    if (gsg->_supports_3d_texture) {
+      GLP(Disable)(GL_TEXTURE_3D);
     }
+    if (gsg->_supports_cube_map) {
+      GLP(Disable)(GL_TEXTURE_CUBE_MAP);
+    }
+    // This is probably faster - but maybe not as safe?
+    // cgGLDisableTextureParameter(p);
   }
 #endif
 }
@@ -643,657 +530,51 @@ update_shader_texture_bindings(CLP(ShaderContext) *prev, GSG *gsg)
 {
   if (prev) prev->disable_shader_texture_bindings(gsg);
 #ifdef HAVE_CGGL
-  if (_cg_context) {
-    for (int i=0; i<(int)_cg_texbind.size(); i++) {
-      Texture *tex = 0;
-      InternalName *id = _cg_texbind[i].name;
-      if (id != 0) {
-        const ShaderInput *input = gsg->_target._shader->get_shader_input(id);
-        tex = input->get_texture();
-      } else {
-        if (_cg_texbind[i].stage >= gsg->_target._texture->get_num_on_stages()) {
-          continue;
-        }
-        TextureStage *stage = gsg->_target._texture->get_on_stage(_cg_texbind[i].stage);
-        tex = gsg->_target._texture->get_on_texture(stage);
-      }
-      if (_cg_texbind[i].suffix != 0) {
-        // The suffix feature is inefficient. It is a temporary hack.
-        if (tex == 0) {
-          continue;
-        }
-        tex = tex->load_related(_cg_texbind[i].suffix);
-      }
-      if ((tex == 0) || (tex->get_texture_type() != _cg_texbind[i].desiredtype)) {
-        continue;
-      }
-      TextureContext *tc = tex->prepare_now(gsg->_prepared_objects, gsg);
-      if (tc == (TextureContext*)NULL) {
-        continue;
-      }
-      int texunit = cgGetParameterResourceIndex(_cg_texbind[i].parameter);
-      gsg->_glActiveTexture(GL_TEXTURE0 + texunit);
-
-      GLenum target = gsg->get_texture_target(tex->get_texture_type());
-      if (target == GL_NONE) {
-        // Unsupported texture mode.
-        continue;
-      }
-      GLP(Enable)(target);
-
-      gsg->apply_texture(tc);
-    }
-  }
-#endif
-}
-
-#ifdef HAVE_CGGL
-////////////////////////////////////////////////////////////////////
-//     Function: GLShaderContext::bind_cg_transform
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-void CLP(ShaderContext)::
-bind_cg_transform(const ShaderTransBind &stb, GSG *gsg)
-{
-  const float *data;
-  CPT(TransformState) src;
-  CPT(TransformState) rel;
-
-  if (stb.src_name == InternalName::get_camera()) {
-    src = TransformState::make_identity();
-  } else if (stb.src_name == InternalName::get_view()) {
-    src = gsg->_inv_cs_transform;
-  } else if (stb.src_name == InternalName::get_model()) {
-    src = gsg->get_transform();
-  } else if (stb.src_name == InternalName::get_world()) {
-    src = gsg->get_scene()->get_world_transform();
-  } else {
-    const ShaderInput *input = gsg->_target._shader->get_shader_input(stb.src_name);
-    if (input->get_nodepath().is_empty()) {
-      src = gsg->get_scene()->get_world_transform();
-    } else {
-      src = gsg->get_scene()->get_world_transform()->
-        compose(input->get_nodepath().get_net_transform());
-    }
-  }
-
-  if (stb.rel_name == InternalName::get_camera()) {
-    rel = TransformState::make_identity();
-  } else if (stb.rel_name == InternalName::get_view()) {
-    rel = gsg->_inv_cs_transform;
-  } else if (stb.rel_name == InternalName::get_model()) {
-    rel = gsg->get_transform();
-  } else if (stb.rel_name == InternalName::get_world()) {
-    rel = gsg->get_scene()->get_world_transform();
-  } else {
-    const ShaderInput *input = gsg->_target._shader->get_shader_input(stb.rel_name);
-    if (input->get_nodepath().is_empty()) {
-      rel = gsg->get_scene()->get_world_transform();
-    } else {
-      rel = gsg->get_scene()->get_world_transform()->
-        compose(input->get_nodepath().get_net_transform());
-    }
-  }
-
-  CPT(TransformState) total = rel->invert_compose(src);
-
-  //  data = src->get_mat().get_data();
-  //  cerr << "Src for " << cgGetParameterName(stb.parameter) << " is\n" <<
-  //    data[ 0] << " " << data[ 1] << " " << data[ 2] << " " << data[ 3] << "\n" <<
-  //    data[ 4] << " " << data[ 5] << " " << data[ 6] << " " << data[ 7] << "\n" <<
-  //    data[ 8] << " " << data[ 9] << " " << data[10] << " " << data[11] << "\n" <<
-  //    data[12] << " " << data[13] << " " << data[14] << " " << data[15] << "\n";
-  //  data = rel->get_mat().get_data();
-  //  cerr << "Rel for " << cgGetParameterName(stb.parameter) << " is\n" <<
-  //    data[ 0] << " " << data[ 1] << " " << data[ 2] << " " << data[ 3] << "\n" <<
-  //    data[ 4] << " " << data[ 5] << " " << data[ 6] << " " << data[ 7] << "\n" <<
-  //    data[ 8] << " " << data[ 9] << " " << data[10] << " " << data[11] << "\n" <<
-  //    data[12] << " " << data[13] << " " << data[14] << " " << data[15] << "\n";
-  //  data = total->get_mat().get_data();
-  //  cerr << "Total for " << cgGetParameterName(stb.parameter) << " is\n" <<
-  //    data[ 0] << " " << data[ 1] << " " << data[ 2] << " " << data[ 3] << "\n" <<
-  //    data[ 4] << " " << data[ 5] << " " << data[ 6] << " " << data[ 7] << "\n" <<
-  //    data[ 8] << " " << data[ 9] << " " << data[10] << " " << data[11] << "\n" <<
-  //    data[12] << " " << data[13] << " " << data[14] << " " << data[15] << "\n";
-  //  data = gsg->_cs_transform->get_mat().get_data();
-  //  cerr << "cs_transform is\n" <<
-  //    data[ 0] << " " << data[ 1] << " " << data[ 2] << " " << data[ 3] << "\n" <<
-  //    data[ 4] << " " << data[ 5] << " " << data[ 6] << " " << data[ 7] << "\n" <<
-  //    data[ 8] << " " << data[ 9] << " " << data[10] << " " << data[11] << "\n" <<
-  //    data[12] << " " << data[13] << " " << data[14] << " " << data[15] << "\n";
-  //  data = gsg->_internal_transform->get_mat().get_data();
-  //  cerr << "internal_transform is\n" <<
-  //    data[ 0] << " " << data[ 1] << " " << data[ 2] << " " << data[ 3] << "\n" <<
-  //    data[ 4] << " " << data[ 5] << " " << data[ 6] << " " << data[ 7] << "\n" <<
-  //    data[ 8] << " " << data[ 9] << " " << data[10] << " " << data[11] << "\n" <<
-  //    data[12] << " " << data[13] << " " << data[14] << " " << data[15] << "\n";
-
-  data = total->get_mat().get_data();
-  switch (stb.trans_piece) {
-  case SHADER_data_matrix: cgGLSetMatrixParameterfc(stb.parameter, data); break;
-  case SHADER_data_transpose:  cgGLSetMatrixParameterfr(stb.parameter, data); break;
-  case SHADER_data_row0: cgGLSetParameter4fv(stb.parameter, data+ 0); break;
-  case SHADER_data_row1: cgGLSetParameter4fv(stb.parameter, data+ 4); break;
-  case SHADER_data_row2: cgGLSetParameter4fv(stb.parameter, data+ 8); break;
-  case SHADER_data_row3: cgGLSetParameter4fv(stb.parameter, data+12); break;
-  case SHADER_data_col0: cgGLSetParameter4f(stb.parameter, data[0], data[4], data[ 8], data[12]); break;
-  case SHADER_data_col1: cgGLSetParameter4f(stb.parameter, data[1], data[5], data[ 9], data[13]); break;
-  case SHADER_data_col2: cgGLSetParameter4f(stb.parameter, data[2], data[6], data[10], data[14]); break;
-  case SHADER_data_col3: cgGLSetParameter4f(stb.parameter, data[3], data[7], data[11], data[15]); break;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Shader::errchk_cg_parameter_words
-//       Access: Public, Static
-//  Description: Make sure the provided Cg parameter contains
-//               the specified number of words.  If not, print
-//               error message and return false.
-////////////////////////////////////////////////////////////////////
-bool CLP(ShaderContext)::
-errchk_cg_parameter_words(CGparameter p, int len)
-{
-  vector_string words;
-  tokenize(cgGetParameterName(p), words, "_");
-  if (words.size() != len) {
-    errchk_cg_output(p, "parameter name has wrong number of words");
-    return false;
-  }
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Shader::errchk_cg_parameter_direction
-//       Access: Public, Static
-//  Description: Make sure the provided Cg parameter has the
-//               correct direction.  If not, print
-//               error message and return false.
-////////////////////////////////////////////////////////////////////
-bool CLP(ShaderContext)::
-errchk_cg_parameter_direction(CGparameter p, CGenum dir)
-{
-  if (cgGetParameterDirection(p) != dir) {
-    if (dir == CG_IN)    errchk_cg_output(p, "parameter should be declared 'in'");
-    if (dir == CG_OUT)   errchk_cg_output(p, "parameter should be declared 'out'");
-    if (dir == CG_INOUT) errchk_cg_output(p, "parameter should be declared 'inout'");
-    return false;
-  }
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Shader::errchk_cg_parameter_variance
-//       Access: Public, Static
-//  Description: Make sure the provided Cg parameter has the
-//               correct variance.  If not, print
-//               error message and return false.
-////////////////////////////////////////////////////////////////////
-bool CLP(ShaderContext)::
-errchk_cg_parameter_variance(CGparameter p, CGenum var)
-{
-  if (cgGetParameterVariability(p) != var) {
-    if (var == CG_UNIFORM)
-      errchk_cg_output(p, "parameter should be declared 'uniform'");
-    if (var == CG_VARYING)
-      errchk_cg_output(p, "parameter should be declared 'varying'");
-    if (var == CG_CONSTANT)
-      errchk_cg_output(p, "parameter should be declared 'const'");
-    return false;
-  }
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Shader::errchk_cg_parameter_prog
-//       Access: Public, Static
-//  Description: Make sure the provided Cg parameter is a part
-//               of the correct program.  If not, print
-//               error message and return false.
-////////////////////////////////////////////////////////////////////
-bool CLP(ShaderContext)::
-errchk_cg_parameter_prog(CGparameter p, CGprogram prog, const string &msg)
-{
-  if (cgGetParameterProgram(p) != prog) {
-    string fmsg = "parameter can only be used in a ";
-    errchk_cg_output(p, fmsg+msg+" program");
-    return false;
-  }
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Shader::errchk_cg_parameter_type
-//       Access: Public, Static
-//  Description: Make sure the provided Cg parameter has the
-//               correct type.  If not, print
-//               error message and return false.
-////////////////////////////////////////////////////////////////////
-bool CLP(ShaderContext)::
-errchk_cg_parameter_type(CGparameter p, CGtype dt)
-{
-  if (cgGetParameterType(p) != dt) {
-    string msg = "parameter should be of type ";
-    errchk_cg_output(p, msg + cgGetTypeString(dt));
-    return false;
-  }
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Shader::errchk_cg_parameter_float
-//       Access: Public, Static
-//  Description: Make sure the provided Cg parameter has
-//               a floating point type.  If not, print
-//               error message and return false.
-////////////////////////////////////////////////////////////////////
-bool CLP(ShaderContext)::
-errchk_cg_parameter_float(CGparameter p)
-{
-  CGtype t = cgGetParameterType(p);
-  if ((t != CG_FLOAT1)&&(t != CG_FLOAT2)&&(t != CG_FLOAT3)&&(t != CG_FLOAT4)) {
-    errchk_cg_output(p, "parameter should have a float type");
-    return false;
-  }
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Shader::errchk_cg_parameter_sampler
-//       Access: Public, Static
-//  Description: Make sure the provided Cg parameter has
-//               a texture type.  If not, print
-//               error message and return false.
-////////////////////////////////////////////////////////////////////
-bool CLP(ShaderContext)::
-errchk_cg_parameter_sampler(CGparameter p)
-{
-  CGtype t = cgGetParameterType(p);
-  if ((t!=CG_SAMPLER1D)&&
-      (t!=CG_SAMPLER2D)&&
-      (t!=CG_SAMPLER3D)&&
-      (t!=CG_SAMPLERCUBE)) {
-    errchk_cg_output(p, "parameter should have a 'sampler' type");
-    return false;
-  }
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Shader::errchk_cg_output
-//       Access: Public, Static
-//  Description: Print an error message including a description
-//               of the specified Cg parameter.
-////////////////////////////////////////////////////////////////////
-void CLP(ShaderContext)::
-errchk_cg_output(CGparameter p, const string &msg)
-{
-  string vstr;
-  CGenum v = cgGetParameterVariability(p);
-  if (v == CG_UNIFORM)  vstr = "uniform ";
-  if (v == CG_VARYING)  vstr = "varying ";
-  if (v == CG_CONSTANT) vstr = "const ";
-
-  string dstr;
-  CGenum d = cgGetParameterDirection(p);
-  if (d == CG_IN)    dstr = "in ";
-  if (d == CG_OUT)   dstr = "out ";
-  if (d == CG_INOUT) dstr = "inout ";
-
-  const char *ts = cgGetTypeString(cgGetParameterType(p));
-
-  string err;
-  string fn = _shader_expansion->get_name();
-  err = fn + ": " + msg + " (" + vstr + dstr + ts + " " + cgGetParameterName(p) + ")\n";
-  _cg_errors = _cg_errors + err + "\n";
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Shader::print_cg_compile_errors
-//       Access: Public, Static
-//  Description: Used only after a Cg compile command, to print
-//               out any error messages that may have occurred
-//               during the Cg shader compilation.  The 'file'
-//               is the name of the file containing the Cg code.
-////////////////////////////////////////////////////////////////////
-void CLP(ShaderContext)::
-print_cg_compile_errors(const string &file, CGcontext ctx)
-{
-  CGerror err = cgGetError();
-  if (err != CG_NO_ERROR) {
-    if (err == CG_COMPILER_ERROR) {
-      string listing = cgGetLastListing(ctx);
-      vector_string errlines;
-      tokenize(listing, errlines, "\n");
-      for (int i=0; i<(int)errlines.size(); i++) {
-        string line = trim(errlines[i]);
-        if (line != "") {
-          _cg_errors += file + " " + errlines[i] + "\n";
-        }
-      }
-    } else {
-      _cg_errors += file + ": " + cgGetErrorString(err) + "\n";
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GLShaderContext::compile_cg_parameter
-//       Access: Public
-//  Description: Analyzes a Cg parameter and decides how to
-//               bind the parameter to some part of panda's
-//               internal state.  Updates one of the cg bind
-//               arrays to cause the binding to occur.
-////////////////////////////////////////////////////////////////////
-bool CLP(ShaderContext)::
-compile_cg_parameter(CGparameter p)
-{
-  string pname = cgGetParameterName(p);
-  if (pname.size() == 0) return true;
-  if (pname[0] == '$') return true;
-  vector_string pieces;
-  tokenize(pname, pieces, "_");
-
-  if (pieces.size() < 2) {
-    errchk_cg_output(p,"invalid parameter name");
-    return false;
-  }
-
-  if (pieces[0] == "vtx") {
-    if ((!errchk_cg_parameter_direction(p, CG_IN)) ||
-        (!errchk_cg_parameter_variance(p, CG_VARYING)) ||
-        (!errchk_cg_parameter_float(p)) ||
-        (!errchk_cg_parameter_prog(p, _cg_program[SHADER_type_vert], "vertex")))
-      return false;
-    ShaderVarying bind;
-    bind.parameter = p;
-    if (pieces.size() == 2) {
-      if (pieces[1]=="position") {
-        bind.name = InternalName::get_vertex();
-        bind.append_uv = -1;
-        _cg_varying.push_back(bind);
-        return true;
-      }
-      if (pieces[1].substr(0,8)=="texcoord") {
-        bind.name = InternalName::get_texcoord();
-        bind.append_uv = atoi(pieces[1].c_str()+8);
-        _cg_varying.push_back(bind);
-        return true;
-      }
-      if (pieces[1].substr(0,7)=="tangent") {
-        bind.name = InternalName::get_tangent();
-        bind.append_uv = atoi(pieces[1].c_str()+7);
-        _cg_varying.push_back(bind);
-        return true;
-      }
-      if (pieces[1].substr(0,8)=="binormal") {
-        bind.name = InternalName::get_binormal();
-        bind.append_uv = atoi(pieces[1].c_str()+8);
-        _cg_varying.push_back(bind);
-        return true;
-      }
-    }
-    bind.name = InternalName::get_root();
-    bind.append_uv = -1;
-    for (int i=1; i<(int)(pieces.size()-0); i++)
-      bind.name = bind.name->append(pieces[i]);
-    _cg_varying.push_back(bind);
-    return true;
-  }
-
-  if ((pieces[0] == "trans")||
-      (pieces[0] == "tpose")||
-      (pieces[0] == "row0")||
-      (pieces[0] == "row1")||
-      (pieces[0] == "row2")||
-      (pieces[0] == "row3")||
-      (pieces[0] == "col0")||
-      (pieces[0] == "col1")||
-      (pieces[0] == "col2")||
-      (pieces[0] == "col3")) {
-    if ((!errchk_cg_parameter_words(p,4)) ||
-        (!errchk_cg_parameter_direction(p, CG_IN)) ||
-        (!errchk_cg_parameter_variance(p, CG_UNIFORM)))
-      return false;
-    if ((pieces[2] != "rel")&&(pieces[2] != "to")) {
-      errchk_cg_output(p, "syntax error");
-      return false;
-    }
-    ShaderTransBind bind;
-    bind.parameter = p;
-
-    if      (pieces[0]=="trans") bind.trans_piece = SHADER_data_matrix;
-    else if (pieces[0]=="tpose") bind.trans_piece = SHADER_data_transpose;
-    else if (pieces[0]=="row0") bind.trans_piece = SHADER_data_row0;
-    else if (pieces[0]=="row1") bind.trans_piece = SHADER_data_row1;
-    else if (pieces[0]=="row2") bind.trans_piece = SHADER_data_row2;
-    else if (pieces[0]=="row3") bind.trans_piece = SHADER_data_row3;
-    else if (pieces[0]=="col0") bind.trans_piece = SHADER_data_col0;
-    else if (pieces[0]=="col1") bind.trans_piece = SHADER_data_col1;
-    else if (pieces[0]=="col2") bind.trans_piece = SHADER_data_col2;
-    else if (pieces[0]=="col3") bind.trans_piece = SHADER_data_col3;
-
-    bind.src_name = InternalName::make(pieces[1]);
-    bind.rel_name = InternalName::make(pieces[3]);
-
-    if ((bind.trans_piece == SHADER_data_matrix)||(bind.trans_piece == SHADER_data_transpose)) {
-      if (!errchk_cg_parameter_type(p, CG_FLOAT4x4)) return false;
-    } else {
-      if (!errchk_cg_parameter_type(p, CG_FLOAT4)) return false;
-    }
-
-    _cg_parameter_bind.push_back(bind);
-    if ((bind.src_name == InternalName::get_model()) ||
-        (bind.rel_name == InternalName::get_model())) {
-      _cg_transform_bind.push_back(bind);
-    }
-    return true;
+  if (_cg_context == 0) {
+    return;
   }
 
-  if ((pieces[0]=="mstrans")||
-      (pieces[0]=="cstrans")||
-      (pieces[0]=="wstrans")||
-      (pieces[0]=="vstrans")||
-      (pieces[0]=="mspos")||
-      (pieces[0]=="cspos")||
-      (pieces[0]=="wspos")||
-      (pieces[0]=="vspos")) {
-    if ((!errchk_cg_parameter_words(p,2)) ||
-        (!errchk_cg_parameter_direction(p, CG_IN)) ||
-        (!errchk_cg_parameter_variance(p, CG_UNIFORM)))
-      return false;
-    ShaderTransBind bind;
-    bind.parameter = p;
-
-    if      (pieces[0]=="wstrans") { bind.rel_name = InternalName::get_world();  bind.trans_piece = SHADER_data_matrix; }
-    else if (pieces[0]=="vstrans") { bind.rel_name = InternalName::get_view();   bind.trans_piece = SHADER_data_matrix; }
-    else if (pieces[0]=="cstrans") { bind.rel_name = InternalName::get_camera(); bind.trans_piece = SHADER_data_matrix; }
-    else if (pieces[0]=="mstrans") { bind.rel_name = InternalName::get_model();  bind.trans_piece = SHADER_data_matrix; }
-    else if (pieces[0]=="wspos")   { bind.rel_name = InternalName::get_world();  bind.trans_piece = SHADER_data_row3; }
-    else if (pieces[0]=="vspos")   { bind.rel_name = InternalName::get_view();   bind.trans_piece = SHADER_data_row3; }
-    else if (pieces[0]=="cspos")   { bind.rel_name = InternalName::get_camera(); bind.trans_piece = SHADER_data_row3; }
-    else if (pieces[0]=="mspos")   { bind.rel_name = InternalName::get_model();  bind.trans_piece = SHADER_data_row3; }
-
-    bind.src_name = InternalName::make(pieces[1]);
-
-    if ((bind.trans_piece == SHADER_data_matrix)||(bind.trans_piece == SHADER_data_transpose)) {
-      if (!errchk_cg_parameter_type(p, CG_FLOAT4x4)) return false;
+  for (int i=0; i<(int)_tex_spec.size(); i++) {
+    CGparameter p = (CGparameter)(_tex_spec[i]._parameter);
+    Texture *tex = 0;
+    InternalName *id = _tex_spec[i]._name;
+    if (id != 0) {
+      const ShaderInput *input = gsg->_target._shader->get_shader_input(id);
+      tex = input->get_texture();
     } else {
-      if (!errchk_cg_parameter_type(p, CG_FLOAT4)) return false;
-    }
-    _cg_parameter_bind.push_back(bind);
-    if ((bind.src_name == InternalName::get_model()) ||
-        (bind.rel_name == InternalName::get_model())) {
-      _cg_transform_bind.push_back(bind);
-    }
-    return true;
-  }
-
-  if ((pieces[0]=="mat")||
-      (pieces[0]=="inv")||
-      (pieces[0]=="tps")||
-      (pieces[0]=="itp")) {
-    if ((!errchk_cg_parameter_words(p,2)) ||
-        (!errchk_cg_parameter_direction(p, CG_IN)) ||
-        (!errchk_cg_parameter_variance(p, CG_UNIFORM)) ||
-        (!errchk_cg_parameter_type(p, CG_FLOAT4x4)))
-      return false;
-    ShaderAutoBind bind;
-    bind.parameter = p;
-    bind.value = 0;
-    if      (pieces[1] == "modelview")  bind.value += 0;
-    else if (pieces[1] == "projection") bind.value += 4;
-    else if (pieces[1] == "texmatrix")  bind.value += 8;
-    else if (pieces[1] == "modelproj")  bind.value += 12;
-    else {
-      errchk_cg_output(p, "unrecognized matrix name");
-      return false;
-    }
-    if      (pieces[0]=="mat") bind.value += 0;
-    else if (pieces[0]=="inv") bind.value += 1;
-    else if (pieces[0]=="tps") bind.value += 2;
-    else if (pieces[0]=="itp") bind.value += 3;
-    _cg_auto_trans.push_back(bind);
-    return true;
-  }
-
-  if (pieces[0] == "sys") {
-    if ((!errchk_cg_parameter_words(p,2)) ||
-        (!errchk_cg_parameter_direction(p, CG_IN)) ||
-        (!errchk_cg_parameter_variance(p, CG_UNIFORM))) {
-      return false;
-    }
-    ShaderAutoBind bind;
-    bind.parameter = p;
-    if (pieces[1] == "pixelsize") {
-      if (!errchk_cg_parameter_type(p, CG_FLOAT2)) {
-        return false;
-      }
-      bind.value = SIC_sys_pixelsize;
-      _cg_auto_param.push_back(bind);
-      return true;
-    }
-    if (pieces[1] == "windowsize") {
-      if (!errchk_cg_parameter_type(p, CG_FLOAT2)) {
-        return false;
+      if (_tex_spec[i]._stage >= gsg->_target._texture->get_num_on_stages()) {
+        continue;
       }
-      bind.value = SIC_sys_windowsize;
-      _cg_auto_param.push_back(bind);
-      return true;
+      TextureStage *stage = gsg->_target._texture->get_on_stage(_tex_spec[i]._stage);
+      tex = gsg->_target._texture->get_on_texture(stage);
     }
-    if (pieces[1] == "cardcenter") {
-      if (!errchk_cg_parameter_type(p, CG_FLOAT2)) {
-        return false;
+    if (_tex_spec[i]._suffix != 0) {
+      // The suffix feature is inefficient. It is a temporary hack.
+      if (tex == 0) {
+        continue;
       }
-      bind.value = SIC_sys_cardcenter;
-      _cg_auto_param.push_back(bind);
-      return true;
-    }
-    errchk_cg_output(p,"unknown system parameter");
-    return false;
-  }
-
-  if (pieces[0] == "tex") {
-    if ((!errchk_cg_parameter_direction(p, CG_IN)) ||
-        (!errchk_cg_parameter_variance(p, CG_UNIFORM)) ||
-        (!errchk_cg_parameter_sampler(p)))
-      return false;
-    if ((pieces.size() != 2)&&(pieces.size() != 3)) {
-      errchk_cg_output(p, "Invalid parameter name");
-      return false;
+      tex = tex->load_related(_tex_spec[i]._suffix);
     }
-    ShaderTexBind bind;
-    bind.parameter = p;
-    bind.name = 0;
-    bind.stage = atoi(pieces[1].c_str());
-    switch (cgGetParameterType(p)) {
-    case CG_SAMPLER1D:   bind.desiredtype = Texture::TT_1d_texture; break;
-    case CG_SAMPLER2D:   bind.desiredtype = Texture::TT_2d_texture; break;
-    case CG_SAMPLER3D:   bind.desiredtype = Texture::TT_3d_texture; break;
-    case CG_SAMPLERCUBE: bind.desiredtype = Texture::TT_cube_map; break;
-    default:
-      errchk_cg_output(p, "Invalid type for a tex-parameter");
-      return false;
+    if ((tex == 0) || (tex->get_texture_type() != _tex_spec[i]._desired_type)) {
+      continue;
     }
-    if (pieces.size()==3) {
-      bind.suffix = InternalName::make(((string)"-") + pieces[2]);
+    TextureContext *tc = tex->prepare_now(gsg->_prepared_objects, gsg);
+    if (tc == (TextureContext*)NULL) {
+      continue;
     }
-    _cg_texbind.push_back(bind);
-    return true;
-  }
-
-  if (pieces[0] == "k") {
-    if ((!errchk_cg_parameter_words(p,2)) ||
-        (!errchk_cg_parameter_direction(p, CG_IN)) ||
-        (!errchk_cg_parameter_variance(p, CG_UNIFORM)))
-      return false;
-    switch (cgGetParameterType(p)) {
-    case CG_FLOAT4: {
-      ShaderArgBind bind;
-      bind.parameter = p;
-      bind.name = InternalName::make(pieces[1]);
-      _cg_fbind.push_back(bind);
-      break;
-    }
-    case CG_FLOAT4x4: {
-      ShaderArgBind bind;
-      bind.parameter = p;
-      bind.name = InternalName::make(pieces[1]);
-      _cg_npbind.push_back(bind);
-      break;
-    }
-    case CG_SAMPLER1D: {
-      ShaderTexBind bind;
-      bind.parameter = p;
-      bind.name = InternalName::make(pieces[1]);
-      bind.desiredtype=Texture::TT_1d_texture;
-      _cg_texbind.push_back(bind);
-      break;
-    }
-    case CG_SAMPLER2D: {
-      ShaderTexBind bind;
-      bind.parameter = p;
-      bind.name = InternalName::make(pieces[1]);
-      bind.desiredtype=Texture::TT_2d_texture;
-      _cg_texbind.push_back(bind);
-      break;
-    }
-    case CG_SAMPLER3D: {
-      ShaderTexBind bind;
-      bind.parameter = p;
-      bind.name = InternalName::make(pieces[1]);
-      bind.desiredtype=Texture::TT_3d_texture;
-      _cg_texbind.push_back(bind);
-      break;
-    }
-    case CG_SAMPLERCUBE: {
-      ShaderTexBind bind;
-      bind.parameter = p;
-      bind.name = InternalName::make(pieces[1]);
-      bind.desiredtype = Texture::TT_cube_map;
-      _cg_texbind.push_back(bind);
-      break;
-    }
-    default:
-      errchk_cg_output(p, "Invalid type for a k-parameter");
-      return false;
+    
+    int texunit = cgGetParameterResourceIndex(p);
+    gsg->_glActiveTexture(GL_TEXTURE0 + texunit);
+    
+    GLenum target = gsg->get_texture_target(tex->get_texture_type());
+    if (target == GL_NONE) {
+      // Unsupported texture mode.
+      continue;
     }
-    return true;
+    GLP(Enable)(target);
+    
+    gsg->apply_texture(tc);
   }
-
-  if (pieces[0] == "l") {
-    // IMPLEMENT THE ERROR CHECKING
-    return true; // Cg handles this automatically.
-  }
-
-  if (pieces[0] == "o") {
-    // IMPLEMENT THE ERROR CHECKING
-    return true; // Cg handles this automatically.
-  }
-
-  errchk_cg_output(p, "unrecognized parameter name");
-  return false;
-}
 #endif
-
+}
 

+ 2 - 78
panda/src/glstuff/glShaderContext_src.h

@@ -42,102 +42,26 @@ public:
   INLINE bool valid(void);
   void bind(GSG *gsg);
   void unbind();
-  void issue_parameters(GSG *gsg);
-  void issue_transform(GSG *gsg);
+  void issue_parameters(GSG *gsg, bool all);
   void disable_shader_vertex_arrays(GSG *gsg);
   void update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg);
   void disable_shader_texture_bindings(GSG *gsg);
   void update_shader_texture_bindings(CLP(ShaderContext) *prev, GSG *gsg);
-
   bool _state;
 
 private:
 
 #ifdef HAVE_CGGL
-  enum ShaderAutoValue {
-    // This first batch of constants cleverly lines up
-    // with the Cg constant values.  Don't insert anything.
-    SIC_mat_modelview,
-    SIC_inv_modelview,
-    SIC_tps_modelview,
-    SIC_itp_modelview,
-    SIC_mat_projection,
-    SIC_inv_projection,
-    SIC_tps_projection,
-    SIC_itp_projection,
-    SIC_mat_texture,
-    SIC_inv_texture,
-    SIC_tps_texture,
-    SIC_itp_texture,
-    SIC_mat_modelproj,
-    SIC_inv_modelproj,
-    SIC_tps_modelproj,
-    SIC_itp_modelproj,
-    // From this point forward, it's okay to insert stuff.
-    SIC_sys_windowsize,
-    SIC_sys_pixelsize,
-    SIC_sys_cardcenter,
-  };
-  struct ShaderAutoBind {
-    CGparameter parameter;
-    int value;
-  };
-  struct ShaderArgBind {
-    CGparameter parameter;
-    PT(InternalName) name;
-  };
-  struct ShaderTexBind {
-    CGparameter parameter;
-    PT(InternalName) name;
-    int stage;
-    int desiredtype;
-    PT(InternalName) suffix;
-  };
-  struct ShaderTransBind {
-    CGparameter parameter;
-    PT(InternalName) src_name;
-    PT(InternalName) rel_name;
-    int trans_piece;
-  };
-  struct ShaderVarying {
-    CGparameter parameter;
-    PT(InternalName) name;
-    int append_uv;
-  };
   CGcontext _cg_context;
   CGprofile _cg_profile[2];
   CGprogram _cg_program[2];
-  string    _cg_errors;
-
-  // These arrays contain lists of "bindings." They
-  // tell us how to fill the shader's input parameters.
-  vector <ShaderAutoBind> _cg_auto_trans;
-  vector <ShaderAutoBind> _cg_auto_param;
-  vector <ShaderArgBind> _cg_fbind;
-  vector <ShaderArgBind> _cg_npbind;
-  vector <ShaderTexBind> _cg_texbind;
-  vector <ShaderTransBind> _cg_transform_bind;
-  vector <ShaderTransBind> _cg_parameter_bind;
-  vector <ShaderVarying> _cg_varying;
 
   bool try_cg_compile(ShaderExpansion *s, GSG *gsg);
-  void bind_cg_transform(const ShaderTransBind &stb,
-                         CLP(GraphicsStateGuardian) *gsg);
   void suggest_cg_profile(const string &vpro, const string &fpro);
   CGprofile parse_cg_profile(const string &id, bool vertex);
-  void issue_cg_auto_bind(const ShaderAutoBind &bind, GSG *gsg);
-  bool compile_cg_parameter(CGparameter p);
-  bool errchk_cg_parameter_words(CGparameter p, int len);
-  bool errchk_cg_parameter_direction(CGparameter p, CGenum dir);
-  bool errchk_cg_parameter_variance(CGparameter p, CGenum var);
-  bool errchk_cg_parameter_prog(CGparameter p, CGprogram prog, const string &msg);
-  bool errchk_cg_parameter_type(CGparameter p, CGtype dt);
-  bool errchk_cg_parameter_float(CGparameter p);
-  bool errchk_cg_parameter_sampler(CGparameter p);
-  void errchk_cg_output(CGparameter p, const string &msg);
   void print_cg_compile_errors(const string &file, CGcontext ctx);
 #endif
-
+  
   void release_resources(void);
 
 public:

+ 32 - 0
panda/src/gobj/lens.I

@@ -442,3 +442,35 @@ operator << (ostream &out, const Lens &lens) {
   lens.output(out);
   return out;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: Lens::get_projection_mat
+//       Access: Published
+//  Description: Returns the complete transformation matrix from a 3-d
+//               point in space to a point on the film, if such a
+//               matrix exists, or the identity matrix if the lens is
+//               nonlinear, in the specified coordinate system.
+////////////////////////////////////////////////////////////////////
+const LMatrix4f Lens::
+get_projection_mat(CoordinateSystem cs, StereoChannel channel) const {
+  return
+    LMatrix4f::convert_mat(cs, _cs) *
+    get_projection_mat(channel) *
+    LMatrix4f::convert_mat(CS_yup_right, cs);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Lens::get_projection_mat_inv
+//       Access: Published
+//  Description: Returns the matrix that transforms from a 2-d point
+//               on the film to a 3-d vector in space, if such a
+//               matrix exists, in the specified coordinate system.
+////////////////////////////////////////////////////////////////////
+const LMatrix4f Lens::
+get_projection_mat_inv(CoordinateSystem cs, StereoChannel channel) const {
+  return
+    LMatrix4f::convert_mat(cs, CS_yup_right) *
+    get_projection_mat_inv(channel) *
+    LMatrix4f::convert_mat(_cs, cs);
+}
+

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

@@ -155,6 +155,9 @@ PUBLISHED:
   const LMatrix4f &get_projection_mat(StereoChannel channel = SC_mono) const;
   const LMatrix4f &get_projection_mat_inv(StereoChannel channel = SC_mono) const;
 
+  INLINE const LMatrix4f get_projection_mat(CoordinateSystem cs, StereoChannel channel) const;
+  INLINE const LMatrix4f get_projection_mat_inv(CoordinateSystem cs, StereoChannel channel) const;
+  
   const LMatrix4f &get_film_mat() const;
   const LMatrix4f &get_film_mat_inv() const;
 

+ 631 - 0
panda/src/gobj/shaderContext.cxx

@@ -18,3 +18,634 @@
 
 TypeHandle ShaderContext::_type_handle;
 
+#ifdef HAVE_CG
+#include "Cg/cg.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::report_cg_compile_errors
+//       Access: Public, Static
+//  Description: Used only after a Cg compile command, to print
+//               out any error messages that may have occurred
+//               during the Cg shader compilation.  The 'file'
+//               is the name of the file containing the Cg code.
+////////////////////////////////////////////////////////////////////
+void ShaderContext::
+report_cg_compile_errors(const string &file, CGcontext ctx,
+                         NotifyCategory *cat)
+{
+  CGerror err = cgGetError();
+  if (err != CG_NO_ERROR) {
+    if (err == CG_COMPILER_ERROR) {
+      string listing = cgGetLastListing(ctx);
+      vector_string errlines;
+      tokenize(listing, errlines, "\n");
+      for (int i=0; i<(int)errlines.size(); i++) {
+        string line = trim(errlines[i]);
+        if (line != "") {
+          cat->error() << file << " " << errlines[i] << "\n";
+        }
+      }
+    } else {
+      cat->error() << file << ": " << cgGetErrorString(err) << "\n";
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::report_cg_parameter_error
+//       Access: Public
+//  Description: Generate an error message including a description
+//               of the specified Cg parameter.
+////////////////////////////////////////////////////////////////////
+void ShaderContext::
+report_cg_parameter_error(CGparameter p, const string &msg)
+{
+  string vstr;
+  CGenum v = cgGetParameterVariability(p);
+  if (v == CG_UNIFORM)  vstr = "uniform ";
+  if (v == CG_VARYING)  vstr = "varying ";
+  if (v == CG_CONSTANT) vstr = "const ";
+
+  string dstr;
+  CGenum d = cgGetParameterDirection(p);
+  if (d == CG_IN)    dstr = "in ";
+  if (d == CG_OUT)   dstr = "out ";
+  if (d == CG_INOUT) dstr = "inout ";
+
+  const char *ts = cgGetTypeString(cgGetParameterType(p));
+
+  string err;
+  string fn = _shader_expansion->get_name();
+  _cg_report_cat->error() << fn << ": " << msg << " (" <<
+    vstr << dstr << ts << " " << cgGetParameterName(p) << ")\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::errchk_cg_parameter_words
+//       Access: Public, Static
+//  Description: Make sure the provided Cg parameter contains
+//               the specified number of words.  If not, print
+//               error message and return false.
+////////////////////////////////////////////////////////////////////
+bool ShaderContext::
+errchk_cg_parameter_words(CGparameter p, int len)
+{
+  vector_string words;
+  tokenize(cgGetParameterName(p), words, "_");
+  if (words.size() != len) {
+    report_cg_parameter_error(p, "parameter name has wrong number of words");
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::errchk_cg_parameter_in
+//       Access: Public, Static
+//  Description: Make sure the provided Cg parameter has the
+//               CG_IN direction.  If not, print
+//               error message and return false.
+////////////////////////////////////////////////////////////////////
+bool ShaderContext::
+errchk_cg_parameter_in(CGparameter p)
+{
+  if (cgGetParameterDirection(p) != CG_IN) {
+    report_cg_parameter_error(p, "parameter should be declared 'in'");
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::errchk_cg_parameter_varying
+//       Access: Public, Static
+//  Description: Make sure the provided Cg parameter has the
+//               correct variance.  If not, print
+//               error message and return false.
+////////////////////////////////////////////////////////////////////
+bool ShaderContext::
+errchk_cg_parameter_varying(CGparameter p)
+{
+  if (cgGetParameterVariability(p) != CG_VARYING) {
+    report_cg_parameter_error(p, "parameter should be declared 'varying'");
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::errchk_cg_parameter_uniform
+//       Access: Public, Static
+//  Description: Make sure the provided Cg parameter has the
+//               correct variance.  If not, print
+//               error message and return false.
+////////////////////////////////////////////////////////////////////
+bool ShaderContext::
+errchk_cg_parameter_uniform(CGparameter p)
+{
+  if (cgGetParameterVariability(p) != CG_UNIFORM) {
+    report_cg_parameter_error(p, "parameter should be declared 'uniform'");
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::errchk_cg_parameter_float
+//       Access: Public, Static
+//  Description: Make sure the provided Cg parameter has
+//               a floating point type.  If not, print
+//               error message and return false.
+////////////////////////////////////////////////////////////////////
+bool ShaderContext::
+errchk_cg_parameter_float(CGparameter p, int lo, int hi)
+{
+  CGtype t = cgGetParameterType(p);
+  int nfloat = 0;
+  switch (t) {
+  case CG_FLOAT1: nfloat = 1; break;
+  case CG_FLOAT2: nfloat = 2; break;
+  case CG_FLOAT3: nfloat = 3; break;
+  case CG_FLOAT4: nfloat = 4; break;
+  case CG_FLOAT4x4: nfloat = 16; break;
+  }
+  if ((nfloat < lo)||(nfloat > hi)) {
+    report_cg_parameter_error(p, "wrong float-type for parameter");
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::errchk_cg_parameter_sampler
+//       Access: Public, Static
+//  Description: Make sure the provided Cg parameter has
+//               a texture type.  If not, print
+//               error message and return false.
+////////////////////////////////////////////////////////////////////
+bool ShaderContext::
+errchk_cg_parameter_sampler(CGparameter p)
+{
+  CGtype t = cgGetParameterType(p);
+  if ((t!=CG_SAMPLER1D)&&
+      (t!=CG_SAMPLER2D)&&
+      (t!=CG_SAMPLER3D)&&
+      (t!=CG_SAMPLERCUBE)) {
+    report_cg_parameter_error(p, "parameter should have a 'sampler' type");
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLShaderContext::parse_cg_trans_clause
+//       Access: Public
+//  Description: Parses a single clause of a "trans" parameter.
+////////////////////////////////////////////////////////////////////
+bool ShaderContext::
+parse_cg_trans_clause(CGparameter p, ShaderMatSpec &spec, const vector_string &pieces,
+                      int &next, ShaderMatOp ofop, ShaderMatOp op) {
+  if (pieces[next+1]=="of") {
+    if (pieces[next+2]=="") {
+      report_cg_parameter_error(p, "'of' should be followed by a name");
+      return false;
+    }
+    if (ofop != SMO_noop) {
+      spec._opcodes.push_back(ofop);
+      spec._args.push_back(InternalName::make(pieces[next+2]));
+    }
+    next += 3;
+    return true;
+  } else {
+    if (op != SMO_noop) {
+      spec._opcodes.push_back(op);
+    }
+    next += 1;
+    return true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLShaderContext::compile_cg_parameter
+//       Access: Public
+//  Description: Analyzes a Cg parameter and decides how to
+//               bind the parameter to some part of panda's
+//               internal state.  Updates one of the cg bind
+//               arrays to cause the binding to occur.
+//
+//               If there is an error, this routine will append
+//               an error message onto the error messages.
+////////////////////////////////////////////////////////////////////
+bool ShaderContext::
+compile_cg_parameter(CGparameter p, NotifyCategory *cat)
+{
+  _cg_report_cat = cat;
+  string pname = cgGetParameterName(p);
+  if (pname.size() == 0) return true;
+  if (pname[0] == '$') return true;
+  vector_string pieces;
+  tokenize(pname, pieces, "_");
+
+  if (pieces.size() < 2) {
+    report_cg_parameter_error(p, "invalid parameter name");
+    return false;
+  }
+
+  // Implement vtx parameters - the varying kind.
+  
+  if (pieces[0] == "vtx") {
+    if ((!errchk_cg_parameter_in(p)) ||
+        (!errchk_cg_parameter_varying(p)) ||
+        (!errchk_cg_parameter_float(p, 1, 4))) {
+      return false;
+    }
+    ShaderVarSpec bind;
+    bind._parameter = (void*)p;
+    if (pieces.size() == 2) {
+      if (pieces[1]=="position") {
+        bind._name = InternalName::get_vertex();
+        bind._append_uv = -1;
+        _var_spec.push_back(bind);
+        return true;
+      }
+      if (pieces[1].substr(0,8)=="texcoord") {
+        bind._name = InternalName::get_texcoord();
+        bind._append_uv = atoi(pieces[1].c_str()+8);
+        _var_spec.push_back(bind);
+        return true;
+      }
+      if (pieces[1].substr(0,7)=="tangent") {
+        bind._name = InternalName::get_tangent();
+        bind._append_uv = atoi(pieces[1].c_str()+7);
+        _var_spec.push_back(bind);
+        return true;
+      }
+      if (pieces[1].substr(0,8)=="binormal") {
+        bind._name = InternalName::get_binormal();
+        bind._append_uv = atoi(pieces[1].c_str()+8);
+        _var_spec.push_back(bind);
+        return true;
+      }
+    }
+    bind._name = InternalName::get_root();
+    bind._append_uv = -1;
+    for (int i=1; i<(int)(pieces.size()-0); i++)
+      bind._name = bind._name->append(pieces[i]);
+    _var_spec.push_back(bind);
+    return true;
+  }
+
+  // Implement some macros. Macros work by altering the
+  // contents of the 'pieces' array, and then falling through.
+
+  if (pieces[0] == "mstrans") {
+    pieces[0] = "trans";
+    pieces.push_back("to");
+    pieces.push_back("model");
+  }
+  if (pieces[0] == "wstrans") {
+    pieces[0] = "trans";
+    pieces.push_back("to");
+    pieces.push_back("world");
+  }
+  if (pieces[0] == "vstrans") {
+    pieces[0] = "trans";
+    pieces.push_back("to");
+    pieces.push_back("view");
+  }
+  if (pieces[0] == "cstrans") {
+    pieces[0] = "trans";
+    pieces.push_back("to");
+    pieces.push_back("clip");
+  }
+  if (pieces[0] == "mspos") {
+    pieces[0] = "row3";
+    pieces.push_back("to");
+    pieces.push_back("model");
+  }
+  if (pieces[0] == "wspos") {
+    pieces[0] = "row3";
+    pieces.push_back("to");
+    pieces.push_back("world");
+  }
+  if (pieces[0] == "vspos") {
+    pieces[0] = "row3";
+    pieces.push_back("to");
+    pieces.push_back("view");
+  }
+  if (pieces[0] == "cspos") {
+    pieces[0] = "row3";
+    pieces.push_back("to");
+    pieces.push_back("clip");
+  }
+
+  // Implement the modelview macros.
+
+  if ((pieces[0] == "mat")||(pieces[0] == "inv")||
+      (pieces[0] == "tps")||(pieces[0] == "itp")) {
+    if (!errchk_cg_parameter_words(p, 2)) {
+      return false;
+    }
+    string trans = pieces[0];
+    string matrix = pieces[1];
+    pieces.clear();
+    if (matrix == "modelview") {
+      tokenize("trans_model_to_apiview", pieces, "_");
+    } else if (matrix == "projection") {
+      tokenize("trans_apiview_to_apiclip", pieces, "_");
+    } else if (matrix == "modelproj") {
+      tokenize("trans_model_to_apiclip", pieces, "_");
+    } else {
+      report_cg_parameter_error(p,"unrecognized matrix name");
+      return false;
+    }
+    if (trans=="mat") {
+      pieces[0] = "trans";
+    } else if (trans=="inv") {
+      string t = pieces[1];
+      pieces[1] = pieces[3];
+      pieces[3] = t;
+    } else if (trans=="tps") {
+      pieces[0] = "tpose";
+    } else if (trans=="itp") {
+      string t = pieces[1];
+      pieces[1] = pieces[3];
+      pieces[3] = t;
+      pieces[0] = "tpose";
+    }
+  }
+
+  // Implement the transform-matrix generator.
+
+  if ((pieces[0]=="trans")||
+      (pieces[0]=="tpose")||
+      (pieces[0]=="row0")||
+      (pieces[0]=="row1")||
+      (pieces[0]=="row2")||
+      (pieces[0]=="row3")||
+      (pieces[0]=="col0")||
+      (pieces[0]=="col1")||
+      (pieces[0]=="col2")||
+      (pieces[0]=="col3")) {
+    
+    if ((!errchk_cg_parameter_in(p)) ||
+        (!errchk_cg_parameter_uniform(p)))
+      return false;
+    
+    ShaderMatSpec bind;
+    bind._parameter = (void*)p;
+    bind._trans_dependent = false;
+
+    int next = 1;
+    pieces.push_back("");
+
+    // Decide whether this is a matrix or vector.
+    if      (pieces[0]=="trans")   bind._piece = SMP_whole;
+    else if (pieces[0]=="tpose")   bind._piece = SMP_whole;
+    else if (pieces[0]=="row0")    bind._piece = SMP_row0;
+    else if (pieces[0]=="row1")    bind._piece = SMP_row1;
+    else if (pieces[0]=="row2")    bind._piece = SMP_row2;
+    else if (pieces[0]=="row3")    bind._piece = SMP_row3;
+    else if (pieces[0]=="col0")    bind._piece = SMP_col0;
+    else if (pieces[0]=="col1")    bind._piece = SMP_col1;
+    else if (pieces[0]=="col2")    bind._piece = SMP_col2;
+    else if (pieces[0]=="col3")    bind._piece = SMP_col3;
+    if (bind._piece == SMP_whole) {
+      if (!errchk_cg_parameter_float(p, 16, 16)) return false;
+    } else {
+      if (!errchk_cg_parameter_float(p, 4, 4)) return false;
+    }
+    
+    // Parse the first half of the clause.
+    bool ok = true;
+    if ((pieces[next]=="")||(pieces[next]=="of")||(pieces[next]=="to")){
+      report_cg_parameter_error(p, "argument missing");
+      return false;
+    } else if (pieces[next] == "world") {
+      bind._opcodes.push_back(SMO_world_to_view);
+      next += 1;
+    } else if (pieces[next] == "model") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_view_x_to_view, SMO_model_to_view);
+    } else if (pieces[next] == "clip") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_clip_x_to_view, SMO_clip_to_view);
+    } else if (pieces[next] == "view") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_view_x_to_view, SMO_identity);
+    } else if (pieces[next] == "apiview") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_apiview_x_to_view, SMO_apiview_to_view);
+    } else if (pieces[next] == "apiclip") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_apiclip_x_to_view, SMO_apiclip_to_view);
+    } else {
+      bind._opcodes.push_back(SMO_view_x_to_view);
+      bind._args.push_back(InternalName::make(pieces[next]));
+      next += 1;
+    }
+
+    // Check for errors in the first clause.
+    if (!ok) {
+      return false;
+    }
+
+    // Check for transform-dependence.
+    if (bind._opcodes.back() == SMO_model_to_view) {
+      bind._trans_dependent = true;
+    }
+
+    // Check for syntactic well-formed-ness.
+    if (pieces[next] != "to") {
+      report_cg_parameter_error(p, "keyword 'to' expected");
+      return false;
+    } else {
+      next += 1;
+    }
+    
+    // Parse the second half of the clause.
+    if ((pieces[next]=="")||(pieces[next]=="of")||(pieces[next]=="to")){
+      report_cg_parameter_error(p, "argument missing");
+      return false;
+    } else if (pieces[next] == "world") {
+      bind._opcodes.push_back(SMO_view_to_world_C);
+      next += 1;
+    } else if (pieces[next] == "model") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_view_to_view_x_C, SMO_view_to_model_C);
+    } else if (pieces[next] == "clip") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_view_to_clip_x_C, SMO_view_to_clip_C);
+    } else if (pieces[next] == "view") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_view_to_view_x_C, SMO_noop);
+    } else if (pieces[next] == "apiview") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_view_to_apiview_x_C, SMO_view_to_apiview_C);
+    } else if (pieces[next] == "apiclip") {
+      ok &= parse_cg_trans_clause(p, bind, pieces, next, SMO_view_to_apiclip_x_C, SMO_view_to_apiclip_C);
+    } else {
+      bind._opcodes.push_back(SMO_view_to_view_x_C);
+      bind._args.push_back(InternalName::make(pieces[next]));
+      next += 1;
+    }
+    
+    // Check for errors in the second clause.
+    if (!ok) {
+      return false;
+    }
+
+    // Check for transform-dependence.
+    if (bind._opcodes.back() == SMO_view_to_model_C) {
+      bind._trans_dependent = true;
+    }
+    
+    // Check for syntactic well-formed-ness.
+    if (pieces[next] != "") {
+      report_cg_parameter_error(p, "end of line expected");
+      return false;
+    }
+    
+    _mat_spec.push_back(bind);
+    return true;
+  }
+
+  // Keywords to access unusual parameters.
+  
+  if (pieces[0] == "sys") {
+    if ((!errchk_cg_parameter_words(p,2)) ||
+        (!errchk_cg_parameter_in(p)) ||
+        (!errchk_cg_parameter_uniform(p))) {
+      return false;
+    }
+    ShaderMatSpec bind;
+    bind._parameter = (void*)p;
+    bind._trans_dependent = false;
+    bind._piece = SMP_row3;
+    if (pieces[1] == "pixelsize") {
+      if (!errchk_cg_parameter_float(p, 2, 2)) {
+        return false;
+      }
+      bind._opcodes.push_back(SMO_pixel_size);
+    } else if (pieces[1] == "windowsize") {
+      if (!errchk_cg_parameter_float(p, 2, 2)) {
+        return false;
+      }
+      bind._opcodes.push_back(SMO_window_size);
+    } else if (pieces[1] == "cardcenter") {
+      if (!errchk_cg_parameter_float(p, 2, 2)) {
+        return false;
+      }
+      bind._opcodes.push_back(SMO_card_center);
+    } else {
+      report_cg_parameter_error(p,"unknown system parameter");
+      return false;
+    }
+    _mat_spec.push_back(bind);
+    return true;
+  }
+  
+  // Keywords to access textures.
+  
+  if (pieces[0] == "tex") {
+    if ((!errchk_cg_parameter_in(p)) ||
+        (!errchk_cg_parameter_uniform(p)) ||
+        (!errchk_cg_parameter_sampler(p)))
+      return false;
+    if ((pieces.size() != 2)&&(pieces.size() != 3)) {
+      report_cg_parameter_error(p, "Invalid parameter name");
+      return false;
+    }
+    ShaderTexSpec bind;
+    bind._parameter = (void*)p;
+    bind._name = 0;
+    bind._stage = atoi(pieces[1].c_str());
+    switch (cgGetParameterType(p)) {
+    case CG_SAMPLER1D:   bind._desired_type = Texture::TT_1d_texture; break;
+    case CG_SAMPLER2D:   bind._desired_type = Texture::TT_2d_texture; break;
+    case CG_SAMPLER3D:   bind._desired_type = Texture::TT_3d_texture; break;
+    case CG_SAMPLERCUBE: bind._desired_type = Texture::TT_cube_map; break;
+    default:
+      report_cg_parameter_error(p, "Invalid type for a tex-parameter");
+      return false;
+    }
+    if (pieces.size()==3) {
+      bind._suffix = InternalName::make(((string)"-") + pieces[2]);
+    }
+    _tex_spec.push_back(bind);
+    return true;
+  }
+
+  // Keywords to access constants.
+
+  if (pieces[0] == "k") {
+    if ((!errchk_cg_parameter_words(p,2)) ||
+        (!errchk_cg_parameter_in(p)) ||
+        (!errchk_cg_parameter_uniform(p)))
+      return false;
+    switch (cgGetParameterType(p)) {
+    case CG_FLOAT4: {
+      ShaderMatSpec bind;
+      bind._parameter = (void*)p;
+      bind._trans_dependent = false;
+      bind._piece = SMP_row3;
+      bind._opcodes.push_back(SMO_vec_constant_x);
+      bind._args.push_back(InternalName::make(pieces[1]));
+      _mat_spec.push_back(bind);
+      break;
+    }
+    case CG_FLOAT4x4: {
+      ShaderMatSpec bind;
+      bind._parameter = (void*)p;
+      bind._trans_dependent = false;
+      bind._piece = SMP_whole;
+      bind._opcodes.push_back(SMO_mat_constant_x);
+      bind._args.push_back(InternalName::make(pieces[1]));
+      _mat_spec.push_back(bind);
+      break;
+    }
+    case CG_SAMPLER1D: {
+      ShaderTexSpec bind;
+      bind._parameter = (void*)p;
+      bind._name = InternalName::make(pieces[1]);
+      bind._desired_type=Texture::TT_1d_texture;
+      _tex_spec.push_back(bind);
+      break;
+    }
+    case CG_SAMPLER2D: {
+      ShaderTexSpec bind;
+      bind._parameter = (void*)p;
+      bind._name = InternalName::make(pieces[1]);
+      bind._desired_type=Texture::TT_2d_texture;
+      _tex_spec.push_back(bind);
+      break;
+    }
+    case CG_SAMPLER3D: {
+      ShaderTexSpec bind;
+      bind._parameter = (void*)p;
+      bind._name = InternalName::make(pieces[1]);
+      bind._desired_type=Texture::TT_3d_texture;
+      _tex_spec.push_back(bind);
+      break;
+    }
+    case CG_SAMPLERCUBE: {
+      ShaderTexSpec bind;
+      bind._parameter = (void*)p;
+      bind._name = InternalName::make(pieces[1]);
+      bind._desired_type = Texture::TT_cube_map;
+      _tex_spec.push_back(bind);
+      break;
+    }
+    default:
+      report_cg_parameter_error(p, "Invalid type for a k-parameter");
+      return false;
+    }
+    return true;
+  }
+
+  if (pieces[0] == "l") {
+    // IMPLEMENT THE ERROR CHECKING
+    return true; // Cg handles this automatically.
+  }
+
+  if (pieces[0] == "o") {
+    // IMPLEMENT THE ERROR CHECKING
+    return true; // Cg handles this automatically.
+  }
+
+  report_cg_parameter_error(p, "unrecognized parameter name");
+  return false;
+}
+
+#endif // HAVE_CG

+ 111 - 15
panda/src/gobj/shaderContext.h

@@ -23,6 +23,13 @@
 #include "internalName.h"
 #include "savedContext.h"
 #include "shaderExpansion.h"
+#ifdef HAVE_CG
+// Instead of including the whole header file, just include these stubs.
+typedef struct _CGcontext *CGcontext;
+typedef struct _CGprogram *CGprogram;
+typedef struct _CGparameter *CGparameter;
+#endif
+
 
 ////////////////////////////////////////////////////////////////////
 //       Class : ShaderContext
@@ -41,29 +48,118 @@ public:
   INLINE ShaderContext(ShaderExpansion *se);
   
   ShaderExpansion *_shader_expansion;
-  
+
 public:
-  // The following declarations represent useful routines
-  // and constants that can be used by shader implementations.
-  
   enum {
     SHADER_type_vert=0,
     SHADER_type_frag=1,
     SHADER_type_both=2,
   };
-  enum {
-    SHADER_data_matrix,
-    SHADER_data_transpose,
-    SHADER_data_row0,
-    SHADER_data_row1,
-    SHADER_data_row2,
-    SHADER_data_row3,
-    SHADER_data_col0,
-    SHADER_data_col1,
-    SHADER_data_col2,
-    SHADER_data_col3,
+  enum ShaderMatOp {
+    SMO_identity,
+
+    SMO_modelview,
+    SMO_projection,
+    SMO_modelproj,
+    
+    SMO_window_size,
+    SMO_pixel_size,
+    SMO_card_center,
+    
+    SMO_mat_constant_x,
+    SMO_vec_constant_x,
+    
+    SMO_world_to_view,
+    SMO_view_to_world_C,
+
+    SMO_model_to_view,
+    SMO_view_to_model_C,
+
+    SMO_apiview_to_view,
+    SMO_view_to_apiview_C,
+
+    SMO_clip_to_view,
+    SMO_view_to_clip_C,
+
+    SMO_apiclip_to_view,
+    SMO_view_to_apiclip_C,
+    
+    SMO_view_x_to_view,
+    SMO_view_to_view_x_C,
+
+    SMO_apiview_x_to_view,
+    SMO_view_to_apiview_x_C,
+
+    SMO_clip_x_to_view,
+    SMO_view_to_clip_x_C,
+
+    SMO_apiclip_x_to_view,
+    SMO_view_to_apiclip_x_C,
+    
+    SMO_transpose,
+    SMO_noop,
+  };
+  enum ShaderMatPiece {
+    SMP_whole,
+    SMP_transpose,
+    SMP_row0,
+    SMP_row1,
+    SMP_row2,
+    SMP_row3,
+    SMP_col0,
+    SMP_col1,
+    SMP_col2,
+    SMP_col3,
+  };
+  struct ShaderMatSpec {
+    pvector<ShaderMatOp>      _opcodes;
+    pvector<PT(InternalName)> _args;
+    ShaderMatPiece            _piece;
+    bool                      _trans_dependent;
+    void                     *_parameter;
+  };
+  struct ShaderTexSpec {
+    PT(InternalName)  _name;
+    int               _stage;
+    int               _desired_type;
+    PT(InternalName)  _suffix;
+    void             *_parameter;
+  };
+  struct ShaderVarSpec {
+    PT(InternalName) _name;
+    int              _append_uv;
+    void            *_parameter;
   };
 
+protected:
+  pvector <ShaderMatSpec> _mat_spec;
+  pvector <ShaderTexSpec> _tex_spec;
+  pvector <ShaderVarSpec> _var_spec;
+  
+#ifdef HAVE_CG
+private:
+  // These functions are only called by 'compile_cg_parameter'
+  NotifyCategory *_cg_report_cat;
+  void report_cg_parameter_error(CGparameter p, const string &msg);
+  bool errchk_cg_parameter_words(CGparameter p, int len);
+  bool errchk_cg_parameter_in(CGparameter p);
+  bool errchk_cg_parameter_varying(CGparameter p);
+  bool errchk_cg_parameter_uniform(CGparameter p);
+  bool errchk_cg_parameter_float(CGparameter p, int lo, int hi);
+  bool errchk_cg_parameter_sampler(CGparameter p);
+  bool parse_cg_trans_clause(CGparameter p,
+                             ShaderMatSpec &spec,
+                             const vector_string &pieces,
+                             int &next,
+                             ShaderMatOp ofop,
+                             ShaderMatOp op);
+
+protected:
+  void report_cg_compile_errors(const string &file, CGcontext ctx,
+                                NotifyCategory *cat);
+  bool compile_cg_parameter(CGparameter p, NotifyCategory *cat);
+#endif
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;