Browse Source

Merge branch 'master' of github.com:panda3d/panda3d into webgl-port

rdb 9 years ago
parent
commit
6d28772ab3
95 changed files with 1066 additions and 2014 deletions
  1. 33 0
      direct/src/p3d/panda3d.pdef
  2. 8 2
      direct/src/showbase/ShowBase.py
  3. 1 0
      dtool/src/cppparser/cppExpression.cxx
  4. 1 0
      dtool/src/cppparser/cppInstanceIdentifier.cxx
  5. 1 0
      dtool/src/cppparser/cppScope.cxx
  6. 1 3
      dtool/src/interrogate/interrogateBuilder.cxx
  7. 1 1
      dtool/src/interrogatedb/py_panda.cxx
  8. 2 1
      dtool/src/interrogatedb/py_panda.h
  9. 1 1
      makepanda/config.in
  10. 2 4
      makepanda/makepanda.py
  11. 45 3
      panda/src/bullet/bulletHeightfieldShape.cxx
  12. 3 0
      panda/src/bullet/bulletHeightfieldShape.h
  13. 2 2
      panda/src/bullet/bulletManifoldPoint.cxx
  14. 1 1
      panda/src/bullet/bulletManifoldPoint.h
  15. 6 2
      panda/src/display/frameBufferProperties.cxx
  16. 5 5
      panda/src/display/graphicsStateGuardian.cxx
  17. 7 0
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  18. 4 0
      panda/src/egg/eggRenderMode.cxx
  19. 2 1
      panda/src/egg/eggRenderMode.h
  20. 5 0
      panda/src/egg2pg/eggLoader.cxx
  21. 4 0
      panda/src/egg2pg/eggRenderState.cxx
  22. 5 3
      panda/src/egg2pg/eggSaver.cxx
  23. 0 35
      panda/src/egldisplay/config_egldisplay.cxx
  24. 0 8
      panda/src/egldisplay/config_egldisplay.h
  25. 0 46
      panda/src/egldisplay/eglGraphicsPipe.I
  26. 1 202
      panda/src/egldisplay/eglGraphicsPipe.cxx
  27. 4 55
      panda/src/egldisplay/eglGraphicsPipe.h
  28. 3 2
      panda/src/egldisplay/eglGraphicsStateGuardian.cxx
  29. 0 8
      panda/src/egldisplay/eglGraphicsWindow.I
  30. 5 1350
      panda/src/egldisplay/eglGraphicsWindow.cxx
  31. 4 54
      panda/src/egldisplay/eglGraphicsWindow.h
  32. 10 2
      panda/src/express/virtualFileMountRamdisk.cxx
  33. 2 0
      panda/src/framework/config_framework.cxx
  34. 1 0
      panda/src/framework/config_framework.h
  35. 5 1
      panda/src/framework/pandaFramework.cxx
  36. 23 6
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  37. 37 6
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  38. 7 0
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  39. 22 19
      panda/src/glstuff/glShaderContext_src.cxx
  40. 0 7
      panda/src/gobj/config_gobj.cxx
  41. 0 1
      panda/src/gobj/config_gobj.h
  42. 58 2
      panda/src/gobj/lens.cxx
  43. 38 0
      panda/src/gobj/matrixLens.cxx
  44. 2 0
      panda/src/gobj/matrixLens.h
  45. 9 6
      panda/src/gobj/texture.I
  46. 25 9
      panda/src/gobj/texture.cxx
  47. 103 48
      panda/src/gobj/texture.h
  48. 8 0
      panda/src/gobj/texturePeeker.I
  49. 72 2
      panda/src/gobj/texturePeeker.cxx
  50. 3 0
      panda/src/gobj/texturePeeker.h
  51. 1 0
      panda/src/particlesystem/colorInterpolationManager.cxx
  52. 24 2
      panda/src/pgraph/camera.cxx
  53. 2 0
      panda/src/pgraph/camera.h
  54. 1 0
      panda/src/pgraph/cullResult.cxx
  55. 1 1
      panda/src/pgraph/cullTraverserData.cxx
  56. 35 5
      panda/src/pgraph/lensNode.cxx
  57. 2 0
      panda/src/pgraph/pandaNode.cxx
  58. 5 4
      panda/src/pgraph/renderState.cxx
  59. 4 3
      panda/src/pgraph/transparencyAttrib.cxx
  60. 1 1
      panda/src/pgraph/transparencyAttrib.h
  61. 21 3
      panda/src/pgraphnodes/lightLensNode.I
  62. 4 6
      panda/src/pgraphnodes/lightLensNode.cxx
  63. 9 1
      panda/src/pgraphnodes/lightLensNode.h
  64. 24 2
      panda/src/pgraphnodes/pointLight.I
  65. 13 2
      panda/src/pgraphnodes/pointLight.cxx
  66. 5 0
      panda/src/pgraphnodes/pointLight.h
  67. 1 0
      panda/src/pgraphnodes/shaderGenerator.cxx
  68. 24 2
      panda/src/pgraphnodes/spotlight.I
  69. 13 2
      panda/src/pgraphnodes/spotlight.cxx
  70. 5 0
      panda/src/pgraphnodes/spotlight.h
  71. 1 1
      panda/src/physx/physxClothDesc.cxx
  72. 31 7
      panda/src/physx/physxSoftBody.cxx
  73. 1 1
      panda/src/physx/physxSoftBodyDesc.cxx
  74. 4 2
      panda/src/physx/physxVehicle.cxx
  75. 4 3
      panda/src/physx/physxVehicle.h
  76. 1 1
      panda/src/physx/physxVehicleDesc.cxx
  77. 6 2
      panda/src/physx/physxWheel.cxx
  78. 5 6
      panda/src/physx/physxWheel.h
  79. 1 1
      panda/src/physx/physxWheelDesc.cxx
  80. 20 13
      panda/src/pipeline/pipeline.cxx
  81. 115 6
      panda/src/pnmimage/pfmFile.cxx
  82. 3 0
      panda/src/pnmimage/pfmFile.h
  83. 2 2
      panda/src/pnmimage/pnmImage.cxx
  84. 2 1
      panda/src/pnmtext/pnmTextMaker.cxx
  85. 34 35
      panda/src/putil/bam.h
  86. 2 1
      panda/src/putil/bamReader.cxx
  87. 16 0
      panda/src/putil/bamWriter.I
  88. 4 0
      panda/src/putil/bamWriter.cxx
  89. 4 0
      panda/src/putil/bamWriter.h
  90. 7 0
      panda/src/putil/config_util.cxx
  91. 1 0
      panda/src/putil/config_util.h
  92. 8 0
      panda/src/putil/loaderOptions.cxx
  93. 1 0
      panda/src/putil/loaderOptions.h
  94. 1 0
      panda/src/text/dynamicTextFont.cxx
  95. 15 0
      panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

+ 33 - 0
direct/src/p3d/panda3d.pdef

@@ -291,6 +291,39 @@ class egg(package):
 plugin-path $EGG_ROOT
 plugin-path $EGG_ROOT
 load-file-type egg pandaegg
 load-file-type egg pandaegg
 load-file-type p3ptloader
 load-file-type p3ptloader
+
+# These are excerpted from the default Confauto.prc file.
+egg-object-type-portal          <Scalar> portal { 1 }
+egg-object-type-polylight       <Scalar> polylight { 1 }
+egg-object-type-seq24           <Switch> { 1 } <Scalar> fps { 24 }
+egg-object-type-seq12           <Switch> { 1 } <Scalar> fps { 12 }
+egg-object-type-indexed         <Scalar> indexed { 1 }
+egg-object-type-seq10           <Switch> { 1 } <Scalar> fps { 10 }
+egg-object-type-seq8            <Switch> { 1 } <Scalar> fps { 8 }
+egg-object-type-seq6            <Switch> { 1 } <Scalar>  fps { 6 }
+egg-object-type-seq4            <Switch> { 1 } <Scalar>  fps { 4 }
+egg-object-type-seq2            <Switch> { 1 } <Scalar>  fps { 2 }
+
+egg-object-type-binary          <Scalar> alpha { binary }
+egg-object-type-dual            <Scalar> alpha { dual }
+egg-object-type-glass           <Scalar> alpha { blend_no_occlude }
+
+egg-object-type-model           <Model> { 1 }
+egg-object-type-dcs             <DCS> { 1 }
+egg-object-type-notouch         <DCS> { no_touch }
+
+egg-object-type-barrier         <Collide> { Polyset descend }
+egg-object-type-sphere          <Collide> { Sphere descend }
+egg-object-type-invsphere       <Collide> { InvSphere descend }
+egg-object-type-tube            <Collide> { Tube descend }
+egg-object-type-trigger         <Collide> { Polyset descend intangible }
+egg-object-type-trigger-sphere  <Collide> { Sphere descend intangible }
+egg-object-type-floor           <Collide> { Polyset descend level }
+egg-object-type-dupefloor       <Collide> { Polyset keep descend level }
+egg-object-type-bubble          <Collide> { Sphere keep descend }
+egg-object-type-ghost           <Scalar> collide-mask { 0 }
+egg-object-type-glow            <Scalar> blend { add }
+egg-object-type-direct-widget   <Scalar> collide-mask { 0x80000000 } <Collide> { Polyset descend }
 """)
 """)
 
 
 class ode(package):
 class ode(package):

+ 8 - 2
direct/src/showbase/ShowBase.py

@@ -532,12 +532,19 @@ class ShowBase(DirectObject.DirectObject):
             del self.winList
             del self.winList
             del self.pipe
             del self.pipe
 
 
-    def makeDefaultPipe(self, printPipeTypes = True):
+    def makeDefaultPipe(self, printPipeTypes = None):
         """
         """
         Creates the default GraphicsPipe, which will be used to make
         Creates the default GraphicsPipe, which will be used to make
         windows unless otherwise specified.
         windows unless otherwise specified.
         """
         """
         assert self.pipe == None
         assert self.pipe == None
+
+        if printPipeTypes is None:
+            # When the user didn't specify an explicit setting, take the value
+            # from the config variable. We could just omit the parameter, however
+            # this way we can keep backward compatibility.
+            printPipeTypes = ConfigVariableBool("print-pipe-types", True)
+
         selection = GraphicsPipeSelection.getGlobalPtr()
         selection = GraphicsPipeSelection.getGlobalPtr()
         if printPipeTypes:
         if printPipeTypes:
             selection.printPipeTypes()
             selection.printPipeTypes()
@@ -567,7 +574,6 @@ class ShowBase(DirectObject.DirectObject):
         Creates all GraphicsPipes that the system knows about and fill up
         Creates all GraphicsPipes that the system knows about and fill up
         self.pipeList with them.
         self.pipeList with them.
         """
         """
-        shouldPrintPipes = 0
         selection = GraphicsPipeSelection.getGlobalPtr()
         selection = GraphicsPipeSelection.getGlobalPtr()
         selection.loadAuxModules()
         selection.loadAuxModules()
 
 

+ 1 - 0
dtool/src/cppparser/cppExpression.cxx

@@ -17,6 +17,7 @@
 #include "cppType.h"
 #include "cppType.h"
 #include "cppSimpleType.h"
 #include "cppSimpleType.h"
 #include "cppPointerType.h"
 #include "cppPointerType.h"
+#include "cppEnumType.h"
 #include "cppConstType.h"
 #include "cppConstType.h"
 #include "cppArrayType.h"
 #include "cppArrayType.h"
 #include "cppPreprocessor.h"
 #include "cppPreprocessor.h"

+ 1 - 0
dtool/src/cppparser/cppInstanceIdentifier.cxx

@@ -17,6 +17,7 @@
 #include "cppArrayType.h"
 #include "cppArrayType.h"
 #include "cppConstType.h"
 #include "cppConstType.h"
 #include "cppFunctionType.h"
 #include "cppFunctionType.h"
+#include "cppSimpleType.h"
 #include "cppParameterList.h"
 #include "cppParameterList.h"
 #include "cppIdentifier.h"
 #include "cppIdentifier.h"
 
 

+ 1 - 0
dtool/src/cppparser/cppScope.cxx

@@ -26,6 +26,7 @@
 #include "cppPreprocessor.h"
 #include "cppPreprocessor.h"
 #include "cppTemplateScope.h"
 #include "cppTemplateScope.h"
 #include "cppClassTemplateParameter.h"
 #include "cppClassTemplateParameter.h"
+#include "cppConstType.h"
 #include "cppFunctionType.h"
 #include "cppFunctionType.h"
 #include "cppUsing.h"
 #include "cppUsing.h"
 #include "cppBisonDefs.h"
 #include "cppBisonDefs.h"

+ 1 - 3
dtool/src/interrogate/interrogateBuilder.cxx

@@ -1870,9 +1870,7 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
   iproperty._scoped_name = descope(make_property->get_local_name(&parser));
   iproperty._scoped_name = descope(make_property->get_local_name(&parser));
 
 
   if (return_type != NULL) {
   if (return_type != NULL) {
-    iproperty._type = get_type(return_type, false);
-    // if (iproperty._type == 0) { parser.warning("cannot determine property
-    // type", make_property->_ident->_loc); }
+    iproperty._type = get_type(TypeManager::unwrap_reference(return_type), false);
   } else {
   } else {
     iproperty._type = 0;
     iproperty._type = 0;
   }
   }

+ 1 - 1
dtool/src/interrogatedb/py_panda.cxx

@@ -183,7 +183,7 @@ void *DTOOL_Call_GetPointerThis(PyObject *self) {
  * In the NDEBUG case, this is simply a #define to _PyErr_OCCURRED() (which is
  * In the NDEBUG case, this is simply a #define to _PyErr_OCCURRED() (which is
  * an undocumented inline version of PyErr_Occurred()).
  * an undocumented inline version of PyErr_Occurred()).
  */
  */
-bool Dtool_CheckErrorOccurred() {
+bool _Dtool_CheckErrorOccurred() {
   if (_PyErr_OCCURRED()) {
   if (_PyErr_OCCURRED()) {
     return true;
     return true;
   }
   }

+ 2 - 1
dtool/src/interrogatedb/py_panda.h

@@ -313,12 +313,13 @@ template<class T> INLINE bool DTOOL_Call_ExtractThisPointer(PyObject *self, T *&
 }
 }
 
 
 // Functions related to error reporting.
 // Functions related to error reporting.
+EXPCL_INTERROGATEDB bool _Dtool_CheckErrorOccurred();
 
 
 #ifdef NDEBUG
 #ifdef NDEBUG
 // _PyErr_OCCURRED is an undocumented inline version of PyErr_Occurred.
 // _PyErr_OCCURRED is an undocumented inline version of PyErr_Occurred.
 #define Dtool_CheckErrorOccurred() (_PyErr_OCCURRED() != NULL)
 #define Dtool_CheckErrorOccurred() (_PyErr_OCCURRED() != NULL)
 #else
 #else
-EXPCL_INTERROGATEDB bool Dtool_CheckErrorOccurred();
+#define Dtool_CheckErrorOccurred() _Dtool_CheckErrorOccurred()
 #endif
 #endif
 
 
 EXPCL_INTERROGATEDB PyObject *Dtool_Raise_AssertionError();
 EXPCL_INTERROGATEDB PyObject *Dtool_Raise_AssertionError();

+ 1 - 1
makepanda/config.in

@@ -41,7 +41,7 @@ framebuffer-software #f
 # consistent with the other framebuffer requirements.
 # consistent with the other framebuffer requirements.
 
 
 depth-bits 1
 depth-bits 1
-color-bits 1
+color-bits 1 1 1
 alpha-bits 0
 alpha-bits 0
 stencil-bits 0
 stencil-bits 0
 multisamples 0
 multisamples 0

+ 2 - 4
makepanda/makepanda.py

@@ -4594,8 +4594,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and PkgSkip("X11")==0 and not RUNTI
   TargetAdd('pandagles_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx')
   TargetAdd('pandagles_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx')
   OPTS=['DIR:panda/metalibs/pandagles', 'BUILDING:PANDAGLES', 'GLES', 'EGL']
   OPTS=['DIR:panda/metalibs/pandagles', 'BUILDING:PANDAGLES', 'GLES', 'EGL']
   TargetAdd('pandagles_pandagles.obj', opts=OPTS, input='pandagles.cxx')
   TargetAdd('pandagles_pandagles.obj', opts=OPTS, input='pandagles.cxx')
-  # Uncomment this as soon as x11-specific stuff is removed from p3egldisplay
-  #TargetAdd('libpandagles.dll', input='p3x11display_composite1.obj')
+  TargetAdd('libpandagles.dll', input='p3x11display_composite1.obj')
   TargetAdd('libpandagles.dll', input='pandagles_pandagles.obj')
   TargetAdd('libpandagles.dll', input='pandagles_pandagles.obj')
   TargetAdd('libpandagles.dll', input='p3glesgsg_config_glesgsg.obj')
   TargetAdd('libpandagles.dll', input='p3glesgsg_config_glesgsg.obj')
   TargetAdd('libpandagles.dll', input='p3glesgsg_glesgsg.obj')
   TargetAdd('libpandagles.dll', input='p3glesgsg_glesgsg.obj')
@@ -4613,8 +4612,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES2")==0 and PkgSkip("X11")==0 and not RUNT
   TargetAdd('pandagles2_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx')
   TargetAdd('pandagles2_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx')
   OPTS=['DIR:panda/metalibs/pandagles2', 'BUILDING:PANDAGLES2', 'GLES2', 'EGL']
   OPTS=['DIR:panda/metalibs/pandagles2', 'BUILDING:PANDAGLES2', 'GLES2', 'EGL']
   TargetAdd('pandagles2_pandagles2.obj', opts=OPTS, input='pandagles2.cxx')
   TargetAdd('pandagles2_pandagles2.obj', opts=OPTS, input='pandagles2.cxx')
-  # Uncomment this as soon as x11-specific stuff is removed from p3egldisplay
-  #TargetAdd('libpandagles2.dll', input='p3x11display_composite1.obj')
+  TargetAdd('libpandagles2.dll', input='p3x11display_composite1.obj')
   TargetAdd('libpandagles2.dll', input='pandagles2_pandagles2.obj')
   TargetAdd('libpandagles2.dll', input='pandagles2_pandagles2.obj')
   TargetAdd('libpandagles2.dll', input='p3gles2gsg_config_gles2gsg.obj')
   TargetAdd('libpandagles2.dll', input='p3gles2gsg_config_gles2gsg.obj')
   TargetAdd('libpandagles2.dll', input='p3gles2gsg_gles2gsg.obj')
   TargetAdd('libpandagles2.dll', input='p3gles2gsg_gles2gsg.obj')

+ 45 - 3
panda/src/bullet/bulletHeightfieldShape.cxx

@@ -16,7 +16,9 @@
 TypeHandle BulletHeightfieldShape::_type_handle;
 TypeHandle BulletHeightfieldShape::_type_handle;
 
 
 /**
 /**
- *
+ * @brief Creates a collision shape suited for terrains from a rectangular image.
+ * @details Stores the image's brightness values in a vector Bullet can use, 
+ *   while rotating it 90 degrees to the right.
  */
  */
 BulletHeightfieldShape::
 BulletHeightfieldShape::
 BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up) {
 BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up) {
@@ -28,8 +30,10 @@ BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAx
 
 
   for (int row=0; row < _num_rows; row++) {
   for (int row=0; row < _num_rows; row++) {
     for (int column=0; column < _num_cols; column++) {
     for (int column=0; column < _num_cols; column++) {
-      _data[_num_cols * row + column] =
-        max_height * image.get_bright(column, _num_cols - row - 1);
+      // Transpose
+      _data[_num_rows * column + row] =
+        // Flip y
+        max_height * image.get_bright(row, _num_cols - column - 1);
     }
     }
   }
   }
 
 
@@ -59,3 +63,41 @@ set_use_diamond_subdivision(bool flag) {
 
 
   return _shape->setUseDiamondSubdivision(flag);
   return _shape->setUseDiamondSubdivision(flag);
 }
 }
+
+/**
+ * @brief Creates a collision shape suited for terrains from a rectangular texture.
+ * @details Alternative constructor intended for use with ShaderTerrainMesh. This will
+ *   do bilinear sampling at the corners of all texels. Also works with textures 
+ *   that are non-power-of-two and/or rectangular.
+ */
+BulletHeightfieldShape::
+BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) {
+
+  _num_rows = tex->get_x_size() + 1;
+  _num_cols = tex->get_y_size() + 1;
+  _data = new float[_num_rows * _num_cols];
+
+  PN_stdfloat step_x = 1.0 / (PN_stdfloat)tex->get_x_size();
+  PN_stdfloat step_y = 1.0 / (PN_stdfloat)tex->get_y_size();
+
+  PT(TexturePeeker) peeker = tex->peek();
+  LColor sample;
+
+  for (int row=0; row < _num_rows; row++) {
+    for (int column=0; column < _num_cols; column++) {
+      if (!peeker->lookup_bilinear(sample, row * step_x, column * step_y)) {
+        bullet_cat.error() << "Could not sample texture." << endl;
+      }
+      // Transpose
+      _data[_num_rows * column + row] = max_height * sample.get_x();
+    }
+  }
+
+  _shape = new btHeightfieldTerrainShape(_num_rows,
+                                         _num_cols,
+                                         _data,
+                                         max_height,
+                                         up,
+                                         true, false);
+  _shape->setUserPointer(this);
+}

+ 3 - 0
panda/src/bullet/bulletHeightfieldShape.h

@@ -21,6 +21,8 @@
 #include "bulletShape.h"
 #include "bulletShape.h"
 
 
 #include "pnmImage.h"
 #include "pnmImage.h"
+#include "texture.h"
+#include "texturePeeker.h"
 
 
 /**
 /**
  *
  *
@@ -29,6 +31,7 @@ class EXPCL_PANDABULLET BulletHeightfieldShape : public BulletShape {
 
 
 PUBLISHED:
 PUBLISHED:
   BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up=Z_up);
   BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up=Z_up);
+  BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up=Z_up);
   INLINE BulletHeightfieldShape(const BulletHeightfieldShape &copy);
   INLINE BulletHeightfieldShape(const BulletHeightfieldShape &copy);
   INLINE void operator = (const BulletHeightfieldShape &copy);
   INLINE void operator = (const BulletHeightfieldShape &copy);
   INLINE ~BulletHeightfieldShape();
   INLINE ~BulletHeightfieldShape();

+ 2 - 2
panda/src/bullet/bulletManifoldPoint.cxx

@@ -89,10 +89,10 @@ get_position_world_on_b() const {
 /**
 /**
  *
  *
  */
  */
-LPoint3 BulletManifoldPoint::
+LVector3 BulletManifoldPoint::
 get_normal_world_on_b() const {
 get_normal_world_on_b() const {
 
 
-  return btVector3_to_LPoint3(_pt.m_normalWorldOnB);
+  return btVector3_to_LVector3(_pt.m_normalWorldOnB);
 }
 }
 
 
 /**
 /**

+ 1 - 1
panda/src/bullet/bulletManifoldPoint.h

@@ -34,7 +34,7 @@ PUBLISHED:
   PN_stdfloat get_applied_impulse() const;
   PN_stdfloat get_applied_impulse() const;
   LPoint3 get_position_world_on_a() const;
   LPoint3 get_position_world_on_a() const;
   LPoint3 get_position_world_on_b() const;
   LPoint3 get_position_world_on_b() const;
-  LPoint3 get_normal_world_on_b() const;
+  LVector3 get_normal_world_on_b() const;
   LPoint3 get_local_point_a() const;
   LPoint3 get_local_point_a() const;
   LPoint3 get_local_point_b() const;
   LPoint3 get_local_point_b() const;
 
 

+ 6 - 2
panda/src/display/frameBufferProperties.cxx

@@ -138,6 +138,9 @@ get_default() {
     break;
     break;
   case 1:
   case 1:
     default_props.set_color_bits(color_bits[0]);
     default_props.set_color_bits(color_bits[0]);
+    default_props.set_red_bits(1);
+    default_props.set_green_bits(1);
+    default_props.set_blue_bits(1);
     break;
     break;
   case 3:
   case 3:
     default_props.set_color_bits(color_bits[0] + color_bits[1] + color_bits[2]);
     default_props.set_color_bits(color_bits[0] + color_bits[1] + color_bits[2]);
@@ -649,13 +652,14 @@ setup_color_texture(Texture *tex) const {
   // as the below one would be generated dynamically by the GSG to reflect the
   // as the below one would be generated dynamically by the GSG to reflect the
   // formats that are supported for render-to-texture.
   // formats that are supported for render-to-texture.
 
 
-  static const int num_formats = 13;
+  static const int num_formats = 15;
   static const struct {
   static const struct {
     unsigned char color_bits, red_bits, green_bits, blue_bits, alpha_bits;
     unsigned char color_bits, red_bits, green_bits, blue_bits, alpha_bits;
     bool has_float;
     bool has_float;
     Texture::Format format;
     Texture::Format format;
   } formats[num_formats] = {
   } formats[num_formats] = {
-  // {  1,  1,  0,  0,  0, false, Texture::F_red},
+    {  1,  1,  0,  0,  0, false, Texture::F_red },
+    {  1,  1,  1,  0,  0, false, Texture::F_rg },
     {  1,  1,  1,  1,  0, false, Texture::F_rgb },
     {  1,  1,  1,  1,  0, false, Texture::F_rgb },
     {  1,  1,  1,  1,  1, false, Texture::F_rgba },
     {  1,  1,  1,  1,  1, false, Texture::F_rgba },
     { 24,  8,  8,  8,  0, false, Texture::F_rgb8 },
     { 24,  8,  8,  8,  0, false, Texture::F_rgb8 },

+ 5 - 5
panda/src/display/graphicsStateGuardian.cxx

@@ -3101,7 +3101,7 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
   if (display_cat.is_debug()) {
   if (display_cat.is_debug()) {
     display_cat.debug()
     display_cat.debug()
       << "Constructing shadow buffer for light '" << light->get_name()
       << "Constructing shadow buffer for light '" << light->get_name()
-      << "', size=" << light->_sb_xsize << "x" << light->_sb_ysize
+      << "', size=" << light->_sb_size[0] << "x" << light->_sb_size[1]
       << ", sort=" << light->_sb_sort << "\n";
       << ", sort=" << light->_sb_sort << "\n";
   }
   }
 
 
@@ -3109,7 +3109,7 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
   FrameBufferProperties fbp;
   FrameBufferProperties fbp;
   fbp.set_depth_bits(shadow_depth_bits);
   fbp.set_depth_bits(shadow_depth_bits);
 
 
-  WindowProperties props = WindowProperties::size(light->_sb_xsize, light->_sb_ysize);
+  WindowProperties props = WindowProperties::size(light->_sb_size[0], light->_sb_size[1]);
   int flags = GraphicsPipe::BF_refuse_window;
   int flags = GraphicsPipe::BF_refuse_window;
   if (is_point) {
   if (is_point) {
     flags |= GraphicsPipe::BF_size_square;
     flags |= GraphicsPipe::BF_size_square;
@@ -3124,13 +3124,13 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
   // error
   // error
   PT(Texture) tex = new Texture(light->get_name());
   PT(Texture) tex = new Texture(light->get_name());
   if (is_point) {
   if (is_point) {
-    if (light->_sb_xsize != light->_sb_ysize) {
+    if (light->_sb_size[0] != light->_sb_size[1]) {
       display_cat.error()
       display_cat.error()
         << "PointLight shadow buffers must have an equal width and height!\n";
         << "PointLight shadow buffers must have an equal width and height!\n";
     }
     }
-    tex->setup_cube_map(light->_sb_xsize, Texture::T_unsigned_byte, Texture::F_depth_component);
+    tex->setup_cube_map(light->_sb_size[0], Texture::T_unsigned_byte, Texture::F_depth_component);
   } else {
   } else {
-    tex->setup_2d_texture(light->_sb_xsize, light->_sb_ysize, Texture::T_unsigned_byte, Texture::F_depth_component);
+    tex->setup_2d_texture(light->_sb_size[0], light->_sb_size[1], Texture::T_unsigned_byte, Texture::F_depth_component);
   }
   }
   tex->make_ram_image();
   tex->make_ram_image();
   sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth);
   sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth);

+ 7 - 0
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -3822,6 +3822,13 @@ do_issue_blending() {
     set_render_state(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
     set_render_state(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
     return;
     return;
 
 
+  case TransparencyAttrib::M_premultiplied_alpha:
+    set_render_state(D3DRS_ALPHABLENDENABLE, TRUE);
+    set_render_state(D3DRS_BLENDOP, D3DBLENDOP_ADD);
+    set_render_state(D3DRS_SRCBLEND, D3DBLEND_ONE);
+    set_render_state(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+    return;
+
   default:
   default:
     dxgsg9_cat.error()
     dxgsg9_cat.error()
       << "invalid transparency mode " << (int)transparency_mode << endl;
       << "invalid transparency mode " << (int)transparency_mode << endl;

+ 4 - 0
panda/src/egg/eggRenderMode.cxx

@@ -183,6 +183,8 @@ string_alpha_mode(const string &string) {
     return AM_binary;
     return AM_binary;
   } else if (cmp_nocase_uh(string, "dual") == 0) {
   } else if (cmp_nocase_uh(string, "dual") == 0) {
     return AM_dual;
     return AM_dual;
+  } else if (cmp_nocase_uh(string, "premultiplied") == 0) {
+    return AM_premultiplied;
   } else {
   } else {
     return AM_unspecified;
     return AM_unspecified;
   }
   }
@@ -260,6 +262,8 @@ ostream &operator << (ostream &out, EggRenderMode::AlphaMode mode) {
     return out << "binary";
     return out << "binary";
   case EggRenderMode::AM_dual:
   case EggRenderMode::AM_dual:
     return out << "dual";
     return out << "dual";
+  case EggRenderMode::AM_premultiplied:
+    return out << "premultiplied";
   }
   }
 
 
   nassertr(false, out);
   nassertr(false, out);

+ 2 - 1
panda/src/egg/eggRenderMode.h

@@ -45,7 +45,8 @@ PUBLISHED:
     AM_ms,      // TransparencyAttrib::M_multisample
     AM_ms,      // TransparencyAttrib::M_multisample
     AM_ms_mask, // TransparencyAttrib::M_multisample_mask
     AM_ms_mask, // TransparencyAttrib::M_multisample_mask
     AM_binary,  // TransparencyAttrib::M_binary
     AM_binary,  // TransparencyAttrib::M_binary
-    AM_dual     // TransparencyAttrib::M_dual
+    AM_dual,    // TransparencyAttrib::M_dual
+    AM_premultiplied // TransparencyAttrib::M_premultiplied_alpha
   };
   };
 
 
   enum DepthWriteMode {
   enum DepthWriteMode {

+ 5 - 0
panda/src/egg2pg/eggLoader.cxx

@@ -950,6 +950,11 @@ load_texture(TextureDef &def, EggTexture *egg_tex) {
     }
     }
   }
   }
 
 
+  // Allow the texture loader to pre-compress the texture.
+  if (egg_tex->get_compression_mode() == EggTexture::CM_on) {
+    options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_allow_compression);
+  }
+
   PT(Texture) tex;
   PT(Texture) tex;
   switch (egg_tex->get_texture_type()) {
   switch (egg_tex->get_texture_type()) {
   case EggTexture::TT_unspecified:
   case EggTexture::TT_unspecified:

+ 4 - 0
panda/src/egg2pg/eggRenderState.cxx

@@ -333,6 +333,10 @@ fill_state(EggPrimitive *egg_prim) {
     add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual));
     add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual));
     break;
     break;
 
 
+  case EggRenderMode::AM_premultiplied:
+    add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_premultiplied_alpha));
+    break;
+
   default:
   default:
     break;
     break;
   }
   }

+ 5 - 3
panda/src/egg2pg/eggSaver.cxx

@@ -686,12 +686,15 @@ convert_primitive(const GeomVertexData *vertex_data,
         break;
         break;
       case TransparencyAttrib::M_alpha:
       case TransparencyAttrib::M_alpha:
         if (has_depthwrite && (depthwrite == DepthWriteAttrib::M_off)) {
         if (has_depthwrite && (depthwrite == DepthWriteAttrib::M_off)) {
-            tex_trans = EggRenderMode::AM_blend_no_occlude;
-                has_depthwrite = false;
+          tex_trans = EggRenderMode::AM_blend_no_occlude;
+          has_depthwrite = false;
         } else {
         } else {
           tex_trans = EggRenderMode::AM_blend;
           tex_trans = EggRenderMode::AM_blend;
         }
         }
         break;
         break;
+      case TransparencyAttrib::M_premultiplied_alpha:
+        tex_trans = EggRenderMode::AM_premultiplied;
+        break;
       case TransparencyAttrib::M_multisample:
       case TransparencyAttrib::M_multisample:
         tex_trans = EggRenderMode::AM_ms;
         tex_trans = EggRenderMode::AM_ms;
         break;
         break;
@@ -705,7 +708,6 @@ convert_primitive(const GeomVertexData *vertex_data,
         tex_trans = EggRenderMode::AM_dual;
         tex_trans = EggRenderMode::AM_dual;
         break;
         break;
       default:  // intentional fall-through
       default:  // intentional fall-through
-      case TransparencyAttrib::M_notused:
         break;
         break;
     }
     }
     if (tex_trans != EggRenderMode::AM_unspecified) {
     if (tex_trans != EggRenderMode::AM_unspecified) {

+ 0 - 35
panda/src/egldisplay/config_egldisplay.cxx

@@ -26,41 +26,6 @@ ConfigureFn(config_egldisplay) {
   init_libegldisplay();
   init_libegldisplay();
 }
 }
 
 
-ConfigVariableString display_cfg
-("display", "",
- PRC_DESC("Specify the X display string for the default display.  If this "
-          "is not specified, $DISPLAY is used."));
-
-ConfigVariableBool x_error_abort
-("x-error-abort", false,
- PRC_DESC("Set this true to trigger and abort (and a stack trace) on receipt "
-          "of an error from the X window system.  This can make it easier "
-          "to discover where these errors are generated."));
-
-ConfigVariableInt x_wheel_up_button
-("x-wheel-up-button", 4,
- PRC_DESC("This is the mouse button index of the wheel_up event: which "
-          "mouse button number does the system report when the mouse wheel "
-          "is rolled one notch up?"));
-
-ConfigVariableInt x_wheel_down_button
-("x-wheel-down-button", 5,
- PRC_DESC("This is the mouse button index of the wheel_down event: which "
-          "mouse button number does the system report when the mouse wheel "
-          "is rolled one notch down?"));
-
-ConfigVariableInt x_wheel_left_button
-("x-wheel-left-button", 6,
- PRC_DESC("This is the mouse button index of the wheel_left event: which "
-          "mouse button number does the system report when one scrolls "
-          "to the left?"));
-
-ConfigVariableInt x_wheel_right_button
-("x-wheel-right-button", 7,
- PRC_DESC("This is the mouse button index of the wheel_right event: which "
-          "mouse button number does the system report when one scrolls "
-          "to the right?"));
-
 /**
 /**
  * Initializes the library.  This must be called at least once before any of
  * Initializes the library.  This must be called at least once before any of
  * the functions or classes in this library can be used.  Normally it will be
  * the functions or classes in this library can be used.  Normally it will be

+ 0 - 8
panda/src/egldisplay/config_egldisplay.h

@@ -39,12 +39,4 @@
   extern EXPCL_PANDAGLES const string get_egl_error_string(int error);
   extern EXPCL_PANDAGLES const string get_egl_error_string(int error);
 #endif
 #endif
 
 
-extern ConfigVariableString display_cfg;
-extern ConfigVariableBool x_error_abort;
-
-extern ConfigVariableInt x_wheel_up_button;
-extern ConfigVariableInt x_wheel_down_button;
-extern ConfigVariableInt x_wheel_left_button;
-extern ConfigVariableInt x_wheel_right_button;
-
 #endif
 #endif

+ 0 - 46
panda/src/egldisplay/eglGraphicsPipe.I

@@ -10,49 +10,3 @@
  * @author rdb
  * @author rdb
  * @date 2009-05-21
  * @date 2009-05-21
  */
  */
-
-/**
- * Returns a pointer to the X display associated with the pipe: the display on
- * which to create the windows.
- */
-INLINE X11_Display *eglGraphicsPipe::
-get_display() const {
-  return _display;
-}
-
-/**
- * Returns the X screen number associated with the pipe.
- */
-INLINE int eglGraphicsPipe::
-get_screen() const {
-  return _screen;
-}
-
-/**
- * Returns the handle to the root window on the pipe's display.
- */
-INLINE X11_Window eglGraphicsPipe::
-get_root() const {
-  return _root;
-}
-
-/**
- * Returns the input method opened for the pipe, or NULL if the input method
- * could not be opened for some reason.
- */
-INLINE XIM eglGraphicsPipe::
-get_im() const {
-  return _im;
-}
-
-/**
- * Returns an invisible Cursor suitable for assigning to windows that have the
- * cursor_hidden property set.
- */
-INLINE X11_Cursor eglGraphicsPipe::
-get_hidden_cursor() {
-  if (_hidden_cursor == None) {
-    make_hidden_cursor();
-  }
-  return _hidden_cursor;
-}

+ 1 - 202
panda/src/egldisplay/eglGraphicsPipe.cxx

@@ -21,67 +21,11 @@
 
 
 TypeHandle eglGraphicsPipe::_type_handle;
 TypeHandle eglGraphicsPipe::_type_handle;
 
 
-bool eglGraphicsPipe::_error_handlers_installed = false;
-eglGraphicsPipe::ErrorHandlerFunc *eglGraphicsPipe::_prev_error_handler;
-eglGraphicsPipe::IOErrorHandlerFunc *eglGraphicsPipe::_prev_io_error_handler;
-
-LightReMutex eglGraphicsPipe::_x_mutex;
-
 /**
 /**
  *
  *
  */
  */
 eglGraphicsPipe::
 eglGraphicsPipe::
-eglGraphicsPipe(const string &display) {
-  string display_spec = display;
-  if (display_spec.empty()) {
-    display_spec = display_cfg;
-  }
-  if (display_spec.empty()) {
-    display_spec = ExecutionEnvironment::get_environment_variable("DISPLAY");
-  }
-  if (display_spec.empty()) {
-    display_spec = ":0.0";
-  }
-
-  // The X docs say we should do this to get international character support
-  // from the keyboard.
-  setlocale(LC_ALL, "");
-
-  // But it's important that we use the "C" locale for numeric formatting,
-  // since all of the internal Panda code assumes this--we need a decimal
-  // point to mean a decimal point.
-  setlocale(LC_NUMERIC, "C");
-
-  _is_valid = false;
-  _supported_types = OT_window | OT_buffer | OT_texture_buffer;
-  _display = NULL;
-  _screen = 0;
-  _root = (X11_Window)NULL;
-  _im = (XIM)NULL;
-  _hidden_cursor = None;
-  _egl_display = NULL;
-
-  install_error_handlers();
-
-  _display = XOpenDisplay(display_spec.c_str());
-  if (!_display) {
-    egldisplay_cat.error()
-      << "Could not open display \"" << display_spec << "\".\n";
-    return;
-  }
-
-  if (!XSupportsLocale()) {
-    egldisplay_cat.warning()
-      << "X does not support locale " << setlocale(LC_ALL, NULL) << "\n";
-  }
-  XSetLocaleModifiers("");
-
-  _screen = DefaultScreen(_display);
-  _root = RootWindow(_display, _screen);
-  _display_width = DisplayWidth(_display, _screen);
-  _display_height = DisplayHeight(_display, _screen);
-  _is_valid = true;
-
+eglGraphicsPipe(const string &display) : x11GraphicsPipe(display) {
   _egl_display = eglGetDisplay((NativeDisplayType) _display);
   _egl_display = eglGetDisplay((NativeDisplayType) _display);
   if (!eglInitialize(_egl_display, NULL, NULL)) {
   if (!eglInitialize(_egl_display, NULL, NULL)) {
     egldisplay_cat.error()
     egldisplay_cat.error()
@@ -94,38 +38,6 @@ eglGraphicsPipe(const string &display) {
       << "Couldn't bind EGL to the OpenGL ES API: "
       << "Couldn't bind EGL to the OpenGL ES API: "
       << get_egl_error_string(eglGetError()) << "\n";
       << get_egl_error_string(eglGetError()) << "\n";
   }
   }
-
-  // Connect to an input method for supporting international text entry.
-  _im = XOpenIM(_display, NULL, NULL, NULL);
-  if (_im == (XIM)NULL) {
-    egldisplay_cat.warning()
-      << "Couldn't open input method.\n";
-  }
-
-  // What styles does the current input method support?
-  /*
-  XIMStyles *im_supported_styles;
-  XGetIMValues(_im, XNQueryInputStyle, &im_supported_styles, NULL);
-
-  for (int i = 0; i < im_supported_styles->count_styles; i++) {
-    XIMStyle style = im_supported_styles->supported_styles[i];
-    cerr << "style " << i << ". " << hex << style << dec << "\n";
-  }
-
-  XFree(im_supported_styles);
-  */
-
-  // Get some X atom numbers.
-  _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", false);
-  _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
-  _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
-  _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false);
-  _net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false);
-  _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
-  _net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false);
-  _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
-  _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
-  _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
 }
 }
 
 
 /**
 /**
@@ -133,13 +45,6 @@ eglGraphicsPipe(const string &display) {
  */
  */
 eglGraphicsPipe::
 eglGraphicsPipe::
 ~eglGraphicsPipe() {
 ~eglGraphicsPipe() {
-  release_hidden_cursor();
-  if (_im) {
-    XCloseIM(_im);
-  }
-  if (_display) {
-    XCloseDisplay(_display);
-  }
   if (_egl_display) {
   if (_egl_display) {
     if (!eglTerminate(_egl_display)) {
     if (!eglTerminate(_egl_display)) {
       egldisplay_cat.error() << "Failed to terminate EGL display: "
       egldisplay_cat.error() << "Failed to terminate EGL display: "
@@ -168,26 +73,6 @@ pipe_constructor() {
   return new eglGraphicsPipe;
   return new eglGraphicsPipe;
 }
 }
 
 
-/**
- * Returns an indication of the thread in which this GraphicsPipe requires its
- * window processing to be performed: typically either the app thread (e.g.
- * X) or the draw thread (Windows).
- */
-GraphicsPipe::PreferredWindowThread
-eglGraphicsPipe::get_preferred_window_thread() const {
-  // Actually, since we're creating the graphics context in open_window() now,
-  // it appears we need to ensure the open_window() call is performed in the
-  // draw thread for now, even though X wants all of its calls to be single-
-  // threaded.
-
-  // This means that all X windows may have to be handled by the same draw
-  // thread, which we didn't intend (though the global _x_mutex may allow them
-  // to be technically served by different threads, even though the actual X
-  // calls will be serialized).  There might be a better way.
-
-  return PWT_draw;
-}
-
 /**
 /**
  * Creates a new window on the pipe, if possible.
  * Creates a new window on the pipe, if possible.
  */
  */
@@ -318,89 +203,3 @@ make_output(const string &name,
   // Nothing else left to try.
   // Nothing else left to try.
   return NULL;
   return NULL;
 }
 }
-
-/**
- * Called once to make an invisible Cursor for return from
- * get_hidden_cursor().
- */
-void eglGraphicsPipe::
-make_hidden_cursor() {
-  nassertv(_hidden_cursor == None);
-
-  unsigned int x_size, y_size;
-  XQueryBestCursor(_display, _root, 1, 1, &x_size, &y_size);
-
-  Pixmap empty = XCreatePixmap(_display, _root, x_size, y_size, 1);
-
-  XColor black;
-  memset(&black, 0, sizeof(black));
-
-  _hidden_cursor = XCreatePixmapCursor(_display, empty, empty,
-                                       &black, &black, x_size, y_size);
-  XFreePixmap(_display, empty);
-}
-
-/**
- * Called once to release the invisible cursor created by
- * make_hidden_cursor().
- */
-void eglGraphicsPipe::
-release_hidden_cursor() {
-  if (_hidden_cursor != None) {
-    XFreeCursor(_display, _hidden_cursor);
-    _hidden_cursor = None;
-  }
-}
-
-/**
- * Installs new Xlib error handler functions if this is the first time this
- * function has been called.  These error handler functions will attempt to
- * reduce Xlib's annoying tendency to shut down the client at the first error.
- * Unfortunately, it is difficult to play nice with the client if it has
- * already installed its own error handlers.
- */
-void eglGraphicsPipe::
-install_error_handlers() {
-  if (_error_handlers_installed) {
-    return;
-  }
-
-  _prev_error_handler = (ErrorHandlerFunc *)XSetErrorHandler(error_handler);
-  _prev_io_error_handler = (IOErrorHandlerFunc *)XSetIOErrorHandler(io_error_handler);
-  _error_handlers_installed = true;
-}
-
-/**
- * This function is installed as the error handler for a non-fatal Xlib error.
- */
-int eglGraphicsPipe::
-error_handler(X11_Display *display, XErrorEvent *error) {
-  static const int msg_len = 80;
-  char msg[msg_len];
-  XGetErrorText(display, error->error_code, msg, msg_len);
-  egldisplay_cat.error()
-    << msg << "\n";
-
-  if (x_error_abort) {
-    abort();
-  }
-
-  // We return to allow the application to continue running, unlike the
-  // default X error handler which exits.
-  return 0;
-}
-
-/**
- * This function is installed as the error handler for a fatal Xlib error.
- */
-int eglGraphicsPipe::
-io_error_handler(X11_Display *display) {
-  egldisplay_cat.fatal()
-    << "X fatal error on display " << (void *)display << "\n";
-
-  // Unfortunately, we can't continue from this function, even if we promise
-  // never to use X again.  We're supposed to terminate without returning, and
-  // if we do return, the caller will exit anyway.  Sigh.  Very poor design on
-  // X's part.
-  return 0;
-}

+ 4 - 55
panda/src/egldisplay/eglGraphicsPipe.h

@@ -15,11 +15,7 @@
 #define EGLGRAPHICSPIPE_H
 #define EGLGRAPHICSPIPE_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "graphicsWindow.h"
-#include "graphicsPipe.h"
-#include "lightMutex.h"
-#include "lightReMutex.h"
-#include "get_x11.h"
+#include "x11GraphicsPipe.h"
 
 
 #ifdef OPENGLES_2
 #ifdef OPENGLES_2
   #include "gles2gsg.h"
   #include "gles2gsg.h"
@@ -46,7 +42,7 @@ class eglGraphicsWindow;
  * This graphics pipe represents the interface for creating OpenGL ES graphics
  * This graphics pipe represents the interface for creating OpenGL ES graphics
  * windows on an X-based (e.g.  Unix) client.
  * windows on an X-based (e.g.  Unix) client.
  */
  */
-class eglGraphicsPipe : public GraphicsPipe {
+class eglGraphicsPipe : public x11GraphicsPipe {
 public:
 public:
   eglGraphicsPipe(const string &display = string());
   eglGraphicsPipe(const string &display = string());
   virtual ~eglGraphicsPipe();
   virtual ~eglGraphicsPipe();
@@ -54,29 +50,6 @@ public:
   virtual string get_interface_name() const;
   virtual string get_interface_name() const;
   static PT(GraphicsPipe) pipe_constructor();
   static PT(GraphicsPipe) pipe_constructor();
 
 
-  INLINE X11_Display *get_display() const;
-  INLINE int get_screen() const;
-  INLINE X11_Window get_root() const;
-  INLINE XIM get_im() const;
-
-  INLINE X11_Cursor get_hidden_cursor();
-
-public:
-  virtual PreferredWindowThread get_preferred_window_thread() const;
-
-public:
-  // Atom specifications.
-  Atom _wm_delete_window;
-  Atom _net_wm_window_type;
-  Atom _net_wm_window_type_splash;
-  Atom _net_wm_window_type_fullscreen;
-  Atom _net_wm_state;
-  Atom _net_wm_state_fullscreen;
-  Atom _net_wm_state_above;
-  Atom _net_wm_state_below;
-  Atom _net_wm_state_add;
-  Atom _net_wm_state_remove;
-
 protected:
 protected:
   virtual PT(GraphicsOutput) make_output(const string &name,
   virtual PT(GraphicsOutput) make_output(const string &name,
                                          const FrameBufferProperties &fb_prop,
                                          const FrameBufferProperties &fb_prop,
@@ -89,40 +62,16 @@ protected:
                                          bool &precertify);
                                          bool &precertify);
 
 
 private:
 private:
-  void make_hidden_cursor();
-  void release_hidden_cursor();
-
-  static void install_error_handlers();
-  static int error_handler(X11_Display *display, XErrorEvent *error);
-  static int io_error_handler(X11_Display *display);
-
-  X11_Display *_display;
-  int _screen;
-  X11_Window _root;
-  XIM _im;
   EGLDisplay _egl_display;
   EGLDisplay _egl_display;
 
 
-  X11_Cursor _hidden_cursor;
-
-  typedef int ErrorHandlerFunc(X11_Display *, XErrorEvent *);
-  typedef int IOErrorHandlerFunc(X11_Display *);
-  static bool _error_handlers_installed;
-  static ErrorHandlerFunc *_prev_error_handler;
-  static IOErrorHandlerFunc *_prev_io_error_handler;
-
-public:
-  // This Mutex protects any X library calls, which all have to be single-
-  // threaded.  In particular, it protects eglMakeCurrent().
-  static LightReMutex _x_mutex;
-
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    GraphicsPipe::init_type();
+    x11GraphicsPipe::init_type();
     register_type(_type_handle, "eglGraphicsPipe",
     register_type(_type_handle, "eglGraphicsPipe",
-                  GraphicsPipe::get_class_type());
+                  x11GraphicsPipe::get_class_type());
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
     return get_class_type();
     return get_class_type();

+ 3 - 2
panda/src/egldisplay/eglGraphicsStateGuardian.cxx

@@ -257,8 +257,9 @@ reset() {
 
 
   // If "Mesa" is present, assume software.  However, if "Mesa DRI" is found,
   // If "Mesa" is present, assume software.  However, if "Mesa DRI" is found,
   // it's actually a Mesa-based OpenGL layer running over a hardware driver.
   // it's actually a Mesa-based OpenGL layer running over a hardware driver.
-  if (_gl_renderer.find("Mesa") != string::npos &&
-      _gl_renderer.find("Mesa DRI") == string::npos) {
+  if (_gl_renderer == "Software Rasterizer" ||
+      (_gl_renderer.find("Mesa") != string::npos &&
+       _gl_renderer.find("Mesa DRI") == string::npos)) {
     // It's Mesa, therefore probably a software context.
     // It's Mesa, therefore probably a software context.
     _fbprops.set_force_software(1);
     _fbprops.set_force_software(1);
     _fbprops.set_force_hardware(0);
     _fbprops.set_force_hardware(0);

+ 0 - 8
panda/src/egldisplay/eglGraphicsWindow.I

@@ -10,11 +10,3 @@
  * @author rdb
  * @author rdb
  * @date 2009-05-21
  * @date 2009-05-21
  */
  */
-
-/**
- * Returns the X11 Window handle.
- */
-INLINE X11_Window eglGraphicsWindow::
-get_xwindow() const {
-  return _xwindow;
-}

+ 5 - 1350
panda/src/egldisplay/eglGraphicsWindow.cxx

@@ -27,17 +27,8 @@
 #include "nativeWindowHandle.h"
 #include "nativeWindowHandle.h"
 #include "get_x11.h"
 #include "get_x11.h"
 
 
-#include <errno.h>
-#include <sys/time.h>
-
-#ifdef HAVE_LINUX_INPUT_H
-#include <linux/input.h>
-#endif
-
 TypeHandle eglGraphicsWindow::_type_handle;
 TypeHandle eglGraphicsWindow::_type_handle;
 
 
-#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
-
 /**
 /**
  *
  *
  */
  */
@@ -49,31 +40,12 @@ eglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
                   int flags,
                   int flags,
                   GraphicsStateGuardian *gsg,
                   GraphicsStateGuardian *gsg,
                   GraphicsOutput *host) :
                   GraphicsOutput *host) :
-  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
+  x11GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
 {
 {
   eglGraphicsPipe *egl_pipe;
   eglGraphicsPipe *egl_pipe;
   DCAST_INTO_V(egl_pipe, _pipe);
   DCAST_INTO_V(egl_pipe, _pipe);
-  _display = egl_pipe->get_display();
-  _screen = egl_pipe->get_screen();
-  _xwindow = (X11_Window)NULL;
-  _ic = (XIC)NULL;
   _egl_display = egl_pipe->_egl_display;
   _egl_display = egl_pipe->_egl_display;
   _egl_surface = 0;
   _egl_surface = 0;
-  _awaiting_configure = false;
-  _wm_delete_window = egl_pipe->_wm_delete_window;
-  _net_wm_window_type = egl_pipe->_net_wm_window_type;
-  _net_wm_window_type_splash = egl_pipe->_net_wm_window_type_splash;
-  _net_wm_window_type_fullscreen = egl_pipe->_net_wm_window_type_fullscreen;
-  _net_wm_state = egl_pipe->_net_wm_state;
-  _net_wm_state_fullscreen = egl_pipe->_net_wm_state_fullscreen;
-  _net_wm_state_above = egl_pipe->_net_wm_state_above;
-  _net_wm_state_below = egl_pipe->_net_wm_state_below;
-  _net_wm_state_add = egl_pipe->_net_wm_state_add;
-  _net_wm_state_remove = egl_pipe->_net_wm_state_remove;
-
-  GraphicsWindowInputDevice device =
-    GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
-  add_input_device(device);
 }
 }
 
 
 /**
 /**
@@ -222,327 +194,6 @@ end_flip() {
   GraphicsWindow::end_flip();
   GraphicsWindow::end_flip();
 }
 }
 
 
-/**
- * Do whatever processing is necessary to ensure that the window responds to
- * user events.  Also, honor any requests recently made via
- * request_properties()
- *
- * This function is called only within the window thread.
- */
-void eglGraphicsWindow::
-process_events() {
-  LightReMutexHolder holder(eglGraphicsPipe::_x_mutex);
-
-  GraphicsWindow::process_events();
-
-  if (_xwindow == (X11_Window)0) {
-    return;
-  }
-
-  poll_raw_mice();
-
-  XEvent event;
-  XKeyEvent keyrelease_event;
-  bool got_keyrelease_event = false;
-
-  while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
-    if (XFilterEvent(&event, None)) {
-      continue;
-    }
-
-    if (got_keyrelease_event) {
-      // If a keyrelease event is immediately followed by a matching keypress
-      // event, that's just key repeat and we should treat the two events
-      // accordingly.  It would be nice if X provided a way to differentiate
-      // between keyrepeat and explicit keypresses more generally.
-      got_keyrelease_event = false;
-
-      if (event.type == KeyPress &&
-          event.xkey.keycode == keyrelease_event.keycode &&
-          (event.xkey.time - keyrelease_event.time <= 1)) {
-        // In particular, we only generate down messages for the repeated
-        // keys, not down-and-up messages.
-        handle_keystroke(event.xkey);
-
-        // We thought about not generating the keypress event, but we need
-        // that repeat for backspace.  Rethink later.
-        handle_keypress(event.xkey);
-        continue;
-
-      } else {
-        // This keyrelease event is not immediately followed by a matching
-        // keypress event, so it's a genuine release.
-        handle_keyrelease(keyrelease_event);
-      }
-    }
-
-    WindowProperties properties;
-    ButtonHandle button;
-
-    switch (event.type) {
-    case ReparentNotify:
-      break;
-
-    case ConfigureNotify:
-      _awaiting_configure = false;
-      if (_properties.get_fixed_size()) {
-        // If the window properties indicate a fixed size only, undo any
-        // attempt by the user to change them.  In X, there doesn't appear to
-        // be a way to universally disallow this directly (although we do set
-        // the min_size and max_size to the same value, which seems to work
-        // for most window managers.)
-        WindowProperties current_props = get_properties();
-        if (event.xconfigure.width != current_props.get_x_size() ||
-            event.xconfigure.height != current_props.get_y_size()) {
-          XWindowChanges changes;
-          changes.width = current_props.get_x_size();
-          changes.height = current_props.get_y_size();
-          int value_mask = (CWWidth | CWHeight);
-          XConfigureWindow(_display, _xwindow, value_mask, &changes);
-        }
-
-      } else {
-        // A normal window may be resized by the user at will.
-        properties.set_size(event.xconfigure.width, event.xconfigure.height);
-        system_changed_properties(properties);
-      }
-      break;
-
-    case ButtonPress:
-      // This refers to the mouse buttons.
-      button = get_mouse_button(event.xbutton);
-      _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
-      _input_devices[0].button_down(button);
-      break;
-
-    case ButtonRelease:
-      button = get_mouse_button(event.xbutton);
-      _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
-      _input_devices[0].button_up(button);
-      break;
-
-    case MotionNotify:
-      _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
-      break;
-
-    case KeyPress:
-      handle_keystroke(event.xkey);
-      handle_keypress(event.xkey);
-      break;
-
-    case KeyRelease:
-      // The KeyRelease can't be processed immediately, because we have to
-      // check first if it's immediately followed by a matching KeyPress
-      // event.
-      keyrelease_event = event.xkey;
-      got_keyrelease_event = true;
-      break;
-
-    case EnterNotify:
-      _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
-      break;
-
-    case LeaveNotify:
-      _input_devices[0].set_pointer_out_of_window();
-      break;
-
-    case FocusIn:
-      properties.set_foreground(true);
-      system_changed_properties(properties);
-      break;
-
-    case FocusOut:
-      properties.set_foreground(false);
-      system_changed_properties(properties);
-      break;
-
-    case UnmapNotify:
-      properties.set_minimized(true);
-      system_changed_properties(properties);
-      break;
-
-    case MapNotify:
-      properties.set_minimized(false);
-      system_changed_properties(properties);
-
-      // Auto-focus the window when it is mapped.
-      XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
-      break;
-
-    case ClientMessage:
-      if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
-        // This is a message from the window manager indicating that the user
-        // has requested to close the window.
-        string close_request_event = get_close_request_event();
-        if (!close_request_event.empty()) {
-          // In this case, the app has indicated a desire to intercept the
-          // request and process it directly.
-          throw_event(close_request_event);
-
-        } else {
-          // In this case, the default case, the app does not intend to
-          // service the request, so we do by closing the window.
-
-          // TODO: don't release the gsg in the window thread.
-          close_window();
-          properties.set_open(false);
-          system_changed_properties(properties);
-        }
-      }
-      break;
-
-    case DestroyNotify:
-      // Apparently, we never get a DestroyNotify on a toplevel window.
-      // Instead, we rely on hints from the window manager (see above).
-      egldisplay_cat.info()
-        << "DestroyNotify\n";
-      break;
-
-    default:
-      egldisplay_cat.error()
-        << "unhandled X event type " << event.type << "\n";
-    }
-  }
-
-  if (got_keyrelease_event) {
-    // This keyrelease event is not immediately followed by a matching
-    // keypress event, so it's a genuine release.
-    handle_keyrelease(keyrelease_event);
-  }
-}
-
-/**
- * Applies the requested set of properties to the window, if possible, for
- * instance to request a change in size or minimization status.
- *
- * The window properties are applied immediately, rather than waiting until
- * the next frame.  This implies that this method may *only* be called from
- * within the window thread.
- *
- * The return value is true if the properties are set, false if they are
- * ignored.  This is mainly useful for derived classes to implement extensions
- * to this function.
- */
-void eglGraphicsWindow::
-set_properties_now(WindowProperties &properties) {
-  if (_pipe == (GraphicsPipe *)NULL) {
-    // If the pipe is null, we're probably closing down.
-    GraphicsWindow::set_properties_now(properties);
-    return;
-  }
-
-  eglGraphicsPipe *egl_pipe;
-  DCAST_INTO_V(egl_pipe, _pipe);
-
-  // Fullscreen mode is implemented with a hint to the window manager.
-  // However, we also implicitly set the origin to (0, 0) and the size to the
-  // desktop size, and request undecorated mode, in case the user has a less-
-  // capable window manager (or no window manager at all).
-  if (properties.get_fullscreen()) {
-    properties.set_undecorated(true);
-    properties.set_origin(0, 0);
-    properties.set_size(egl_pipe->get_display_width(),
-                        egl_pipe->get_display_height());
-  }
-
-  GraphicsWindow::set_properties_now(properties);
-  if (!properties.is_any_specified()) {
-    // The base class has already handled this case.
-    return;
-  }
-
-  // The window is already open; we are limited to what we can change on the
-  // fly.
-
-  // We'll pass some property requests on as a window manager hint.
-  WindowProperties wm_properties = _properties;
-  wm_properties.add_properties(properties);
-
-  // The window title may be changed by issuing another hint request.  Assume
-  // this will be honored.
-  if (properties.has_title()) {
-    _properties.set_title(properties.get_title());
-    properties.clear_title();
-  }
-
-  // Ditto for fullscreen mode.
-  if (properties.has_fullscreen()) {
-    _properties.set_fullscreen(properties.get_fullscreen());
-    properties.clear_fullscreen();
-  }
-
-  // The size and position of an already-open window are changed via explicit
-  // X calls.  These may still get intercepted by the window manager.  Rather
-  // than changing _properties immediately, we'll wait for the ConfigureNotify
-  // message to come back.
-  XWindowChanges changes;
-  int value_mask = 0;
-
-  if (properties.has_origin()) {
-    changes.x = properties.get_x_origin();
-    changes.y = properties.get_y_origin();
-    value_mask |= (CWX | CWY);
-    properties.clear_origin();
-  }
-  if (properties.has_size()) {
-    changes.width = properties.get_x_size();
-    changes.height = properties.get_y_size();
-    value_mask |= (CWWidth | CWHeight);
-    properties.clear_size();
-  }
-  if (properties.has_z_order()) {
-    // We'll send the classic stacking request through the standard interface,
-    // for users of primitive window managers; but we'll also send it as a
-    // window manager hint, for users of modern window managers.
-    _properties.set_z_order(properties.get_z_order());
-    switch (properties.get_z_order()) {
-    case WindowProperties::Z_bottom:
-      changes.stack_mode = Below;
-      break;
-
-    case WindowProperties::Z_normal:
-      changes.stack_mode = TopIf;
-      break;
-
-    case WindowProperties::Z_top:
-      changes.stack_mode = Above;
-      break;
-    }
-
-    value_mask |= (CWStackMode);
-    properties.clear_z_order();
-  }
-
-  if (value_mask != 0) {
-    XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
-
-    // Don't draw anything until this is done reconfiguring.
-    _awaiting_configure = true;
-  }
-
-  // We hide the cursor by setting it to an invisible pixmap.
-  if (properties.has_cursor_hidden()) {
-    _properties.set_cursor_hidden(properties.get_cursor_hidden());
-    if (properties.get_cursor_hidden()) {
-      XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor());
-    } else {
-      XDefineCursor(_display, _xwindow, None);
-    }
-    properties.clear_cursor_hidden();
-  }
-
-  if (properties.has_foreground()) {
-    if (properties.get_foreground()) {
-      XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
-    } else {
-      XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
-    }
-    properties.clear_foreground();
-  }
-
-  set_wm_properties(wm_properties, true);
-}
-
 /**
 /**
  * Closes the window right now.  Called from the window thread.
  * Closes the window right now.  Called from the window thread.
  */
  */
@@ -606,97 +257,19 @@ open_window() {
     }
     }
   }
   }
 
 
-  XVisualInfo *visual_info = eglgsg->_visual;
-  if (visual_info == NULL) {
+  _visual_info = eglgsg->_visual;
+  if (_visual_info == NULL) {
     // No X visual for this fbconfig; how can we open the window?
     // No X visual for this fbconfig; how can we open the window?
     egldisplay_cat.error()
     egldisplay_cat.error()
       << "No X visual: cannot open window.\n";
       << "No X visual: cannot open window.\n";
     return false;
     return false;
   }
   }
-  Visual *visual = visual_info->visual;
-  int depth = visual_info->depth;
-
-  if (!_properties.has_origin()) {
-    _properties.set_origin(0, 0);
-  }
-  if (!_properties.has_size()) {
-    _properties.set_size(100, 100);
-  }
-
-  X11_Window parent_window = egl_pipe->get_root();
-  WindowHandle *window_handle = _properties.get_parent_window();
-  if (window_handle != NULL) {
-    egldisplay_cat.info()
-      << "Got parent_window " << *window_handle << "\n";
-    WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
-    if (os_handle != NULL) {
-      egldisplay_cat.info()
-        << "os_handle type " << os_handle->get_type() << "\n";
-
-      if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
-        NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
-        parent_window = x11_handle->get_handle();
-      } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
-        NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
-        parent_window = (X11_Window)int_handle->get_handle();
-      }
-    }
-  }
-  _parent_window_handle = window_handle;
-
-  setup_colormap(visual_info);
-
-  _event_mask =
-    ButtonPressMask | ButtonReleaseMask |
-    KeyPressMask | KeyReleaseMask |
-    EnterWindowMask | LeaveWindowMask |
-    PointerMotionMask |
-    FocusChangeMask |
-    StructureNotifyMask;
-
-  // Initialize window attributes
-  XSetWindowAttributes wa;
-  wa.background_pixel = XBlackPixel(_display, _screen);
-  wa.border_pixel = 0;
-  wa.colormap = _colormap;
-  wa.event_mask = _event_mask;
-
-  unsigned long attrib_mask =
-    CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
 
 
-  _xwindow = XCreateWindow
-    (_display, parent_window,
-     _properties.get_x_origin(), _properties.get_y_origin(),
-     _properties.get_x_size(), _properties.get_y_size(),
-     0, depth, InputOutput, visual, attrib_mask, &wa);
+  setup_colormap(_visual_info);
 
 
-  if (_xwindow == (X11_Window)0) {
-    egldisplay_cat.error()
-      << "failed to create X window.\n";
+  if (!x11GraphicsWindow::open_window()) {
     return false;
     return false;
   }
   }
-  set_wm_properties(_properties, false);
-
-  // We don't specify any fancy properties of the XIC.  It would be nicer if
-  // we could support fancy IM's that want preedit callbacks, etc., but that
-  // can wait until we have an X server that actually supports these to test
-  // it on.
-  XIM im = egl_pipe->get_im();
-  _ic = NULL;
-  if (im) {
-    _ic = XCreateIC
-      (im,
-       XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
-       (void*)NULL);
-    if (_ic == (XIC)NULL) {
-      egldisplay_cat.warning()
-        << "Couldn't create input context.\n";
-    }
-  }
-
-  if (_properties.get_cursor_hidden()) {
-    XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor());
-  }
 
 
   _egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, NULL);
   _egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, NULL);
   if (eglGetError() != EGL_SUCCESS) {
   if (eglGetError() != EGL_SUCCESS) {
@@ -721,923 +294,5 @@ open_window() {
   }
   }
   _fb_properties = eglgsg->get_fb_properties();
   _fb_properties = eglgsg->get_fb_properties();
 
 
-  XMapWindow(_display, _xwindow);
-
-  if (_properties.get_raw_mice()) {
-    open_raw_mice();
-  } else {
-    if (egldisplay_cat.is_debug()) {
-      egldisplay_cat.debug()
-        << "Raw mice not requested.\n";
-    }
-  }
-
-  // Create a WindowHandle for ourselves
-  _window_handle = NativeWindowHandle::make_x11(_xwindow);
-
-  // And tell our parent window that we're now its child.
-  if (_parent_window_handle != (WindowHandle *)NULL) {
-    _parent_window_handle->attach_child(_window_handle);
-  }
-
   return true;
   return true;
 }
 }
-
-/**
- * Asks the window manager to set the appropriate properties.  In X, these
- * properties cannot be specified directly by the application; they must be
- * requested via the window manager, which may or may not choose to honor the
- * request.
- *
- * If already_mapped is true, the window has already been mapped (manifested)
- * on the display.  This means we may need to use a different action in some
- * cases.
- */
-void eglGraphicsWindow::
-set_wm_properties(const WindowProperties &properties, bool already_mapped) {
-  // Name the window if there is a name
-  XTextProperty window_name;
-  XTextProperty *window_name_p = (XTextProperty *)NULL;
-  if (properties.has_title()) {
-    char *name = (char *)properties.get_title().c_str();
-    if (XStringListToTextProperty(&name, 1, &window_name) != 0) {
-      window_name_p = &window_name;
-    }
-  }
-
-  // The size hints request a window of a particular size andor a particular
-  // placement onscreen.
-  XSizeHints *size_hints_p = NULL;
-  if (properties.has_origin() || properties.has_size()) {
-    size_hints_p = XAllocSizeHints();
-    if (size_hints_p != (XSizeHints *)NULL) {
-      if (properties.has_origin()) {
-        size_hints_p->x = properties.get_x_origin();
-        size_hints_p->y = properties.get_y_origin();
-        size_hints_p->flags |= USPosition;
-      }
-      if (properties.has_size()) {
-        size_hints_p->width = properties.get_x_size();
-        size_hints_p->height = properties.get_y_size();
-        size_hints_p->flags |= USSize;
-
-        if (properties.get_fixed_size()) {
-          size_hints_p->min_width = properties.get_x_size();
-          size_hints_p->min_height = properties.get_y_size();
-          size_hints_p->max_width = properties.get_x_size();
-          size_hints_p->max_height = properties.get_y_size();
-          size_hints_p->flags |= (PMinSize | PMaxSize);
-        }
-      }
-    }
-  }
-
-  // The window manager hints include requests to the window manager other
-  // than those specific to window geometry.
-  XWMHints *wm_hints_p = NULL;
-  wm_hints_p = XAllocWMHints();
-  if (wm_hints_p != (XWMHints *)NULL) {
-    if (properties.has_minimized() && properties.get_minimized()) {
-      wm_hints_p->initial_state = IconicState;
-    } else {
-      wm_hints_p->initial_state = NormalState;
-    }
-    wm_hints_p->flags = StateHint;
-  }
-
-  // Two competing window manager interfaces have evolved.  One of them allows
-  // to set certain properties as a "type"; the other one as a "state".  We'll
-  // try to honor both.
-  static const int max_type_data = 32;
-  PN_int32 type_data[max_type_data];
-  int next_type_data = 0;
-
-  static const int max_state_data = 32;
-  PN_int32 state_data[max_state_data];
-  int next_state_data = 0;
-
-  static const int max_set_data = 32;
-  class SetAction {
-  public:
-    inline SetAction() { }
-    inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
-    Atom _state;
-    Atom _action;
-  };
-  SetAction set_data[max_set_data];
-  int next_set_data = 0;
-
-  if (properties.get_fullscreen()) {
-    // For a "fullscreen" request, we pass this through, hoping the window
-    // manager will support EWMH.
-    type_data[next_type_data++] = _net_wm_window_type_fullscreen;
-
-    // We also request it as a state.
-    state_data[next_state_data++] = _net_wm_state_fullscreen;
-    set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_add);
-  } else {
-    set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_remove);
-  }
-
-  // If we asked for a window without a border, there's no excellent way to
-  // arrange that.  For users whose window managers follow the EWMH
-  // specification, we can ask for a "splash" screen, which is usually
-  // undecorated.  It's not exactly right, but the spec doesn't give us an
-  // exactly-right option.
-
-  // For other users, we'll totally punt and just set the window's Class to
-  // "Undecorated", and let the user configure hisher window manager not to
-  // put a border around windows of this class.
-  XClassHint *class_hints_p = NULL;
-  if (properties.get_undecorated()) {
-    class_hints_p = XAllocClassHint();
-    class_hints_p->res_class = (char*) "Undecorated";
-
-    if (!properties.get_fullscreen()) {
-      type_data[next_type_data++] = _net_wm_window_type_splash;
-    }
-  }
-
-  if (properties.has_z_order()) {
-    switch (properties.get_z_order()) {
-    case WindowProperties::Z_bottom:
-      state_data[next_state_data++] = _net_wm_state_below;
-      set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_add);
-      set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
-      break;
-
-    case WindowProperties::Z_normal:
-      set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
-      set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove);
-      break;
-
-    case WindowProperties::Z_top:
-      state_data[next_state_data++] = _net_wm_state_above;
-      set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove);
-      set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_add);
-      break;
-    }
-  }
-
-  nassertv(next_type_data < max_type_data);
-  nassertv(next_state_data < max_state_data);
-  nassertv(next_set_data < max_set_data);
-
-  XChangeProperty(_display, _xwindow, _net_wm_window_type,
-                  XA_ATOM, 32, PropModeReplace,
-                  (unsigned char *)type_data, next_type_data);
-
-  // Request the state properties all at once.
-  XChangeProperty(_display, _xwindow, _net_wm_state,
-                  XA_ATOM, 32, PropModeReplace,
-                  (unsigned char *)state_data, next_state_data);
-
-  if (already_mapped) {
-    // We have to request state changes differently when the window has been
-    // mapped.  To do this, we need to send a client message to the root
-    // window for each change.
-
-    eglGraphicsPipe *egl_pipe;
-    DCAST_INTO_V(egl_pipe, _pipe);
-
-    for (int i = 0; i < next_set_data; ++i) {
-      XClientMessageEvent event;
-      memset(&event, 0, sizeof(event));
-
-      event.type = ClientMessage;
-      event.send_event = True;
-      event.display = _display;
-      event.window = _xwindow;
-      event.message_type = _net_wm_state;
-      event.format = 32;
-      event.data.l[0] = set_data[i]._action;
-      event.data.l[1] = set_data[i]._state;
-      event.data.l[2] = 0;
-      event.data.l[3] = 1;
-
-      XSendEvent(_display, egl_pipe->get_root(), True, 0, (XEvent *)&event);
-    }
-  }
-
-  XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
-                   NULL, 0, size_hints_p, wm_hints_p, class_hints_p);
-
-  if (size_hints_p != (XSizeHints *)NULL) {
-    XFree(size_hints_p);
-  }
-  if (wm_hints_p != (XWMHints *)NULL) {
-    XFree(wm_hints_p);
-  }
-  if (class_hints_p != (XClassHint *)NULL) {
-    XFree(class_hints_p);
-  }
-
-  // Also, indicate to the window manager that we'd like to get a chance to
-  // close our windows cleanly, rather than being rudely disconnected from the
-  // X server if the user requests a window close.
-  Atom protocols[] = {
-    _wm_delete_window,
-  };
-
-  XSetWMProtocols(_display, _xwindow, protocols,
-                  sizeof(protocols) / sizeof(Atom));
-}
-
-/**
- * Allocates a colormap appropriate to the visual and stores in in the
- * _colormap method.
- */
-void eglGraphicsWindow::
-setup_colormap(XVisualInfo *visual) {
-  eglGraphicsPipe *egl_pipe;
-  DCAST_INTO_V(egl_pipe, _pipe);
-  X11_Window root_window = egl_pipe->get_root();
-
-  int visual_class = visual->c_class;
-  int rc, is_rgb;
-
-  switch (visual_class) {
-    case PseudoColor:
-      _colormap = XCreateColormap(_display, root_window,
-                                  visual->visual, AllocAll);
-      break;
-    case TrueColor:
-    case DirectColor:
-      _colormap = XCreateColormap(_display, root_window,
-                                  visual->visual, AllocNone);
-      break;
-    case StaticColor:
-    case StaticGray:
-    case GrayScale:
-      _colormap = XCreateColormap(_display, root_window,
-                                  visual->visual, AllocNone);
-      break;
-    default:
-      egldisplay_cat.error()
-        << "Could not allocate a colormap for visual class "
-        << visual_class << ".\n";
-      break;
-  }
-}
-
-/**
- * Adds raw mice to the _input_devices list.
- */
-void eglGraphicsWindow::
-open_raw_mice()
-{
-#ifdef HAVE_LINUX_INPUT_H
-  bool any_present = false;
-  bool any_mice = false;
-
-  for (int i=0; i<64; i++) {
-    uint8_t evtypes[EV_MAX/8 + 1];
-    ostringstream fnb;
-    fnb << "/dev/input/event" << i;
-    string fn = fnb.str();
-    int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
-    if (fd >= 0) {
-      any_present = true;
-      char name[256];
-      char phys[256];
-      char uniq[256];
-      if ((ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)||
-          (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0)||
-          (ioctl(fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0)||
-          (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) {
-        close(fd);
-        egldisplay_cat.error() <<
-          "Opening raw mice: ioctl failed on " << fn << "\n";
-      } else {
-        if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) {
-          for (char *p=name; *p; p++) {
-            if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
-              *p = '_';
-            }
-          }
-          for (char *p=uniq; *p; p++) {
-            if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
-              *p = '_';
-            }
-          }
-          string full_id = ((string)name) + "." + uniq;
-          MouseDeviceInfo inf;
-          inf._fd = fd;
-          inf._input_device_index = _input_devices.size();
-          inf._io_buffer = "";
-          _mouse_device_info.push_back(inf);
-          GraphicsWindowInputDevice device =
-            GraphicsWindowInputDevice::pointer_only(this, full_id);
-          add_input_device(device);
-          egldisplay_cat.info() << "Raw mouse " <<
-            inf._input_device_index << " detected: " << full_id << "\n";
-          any_mice = true;
-        } else {
-          close(fd);
-        }
-      }
-    } else {
-      if ((errno == ENOENT)||(errno == ENOTDIR)) {
-        break;
-      } else {
-        any_present = true;
-        egldisplay_cat.error() <<
-          "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
-      }
-    }
-  }
-
-  if (!any_present) {
-    egldisplay_cat.error() <<
-      "Opening raw mice: files not found: /dev/input/event*\n";
-  } else if (!any_mice) {
-    egldisplay_cat.error() <<
-      "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
-  }
-#else
-  egldisplay_cat.error() <<
-    "Opening raw mice: panda not compiled with raw mouse support.\n";
-#endif
-}
-
-/**
- * Reads events from the raw mouse device files.
- */
-void eglGraphicsWindow::
-poll_raw_mice()
-{
-#ifdef HAVE_LINUX_INPUT_H
-  for (int dev=0; dev<_mouse_device_info.size(); dev++) {
-    MouseDeviceInfo &inf = _mouse_device_info[dev];
-
-    // Read all bytes into buffer.
-    if (inf._fd >= 0) {
-      while (1) {
-        char tbuf[1024];
-        int nread = read(inf._fd, tbuf, sizeof(tbuf));
-        if (nread > 0) {
-          inf._io_buffer += string(tbuf, nread);
-        } else {
-          if ((nread < 0)&&((errno == EWOULDBLOCK) || (errno==EAGAIN))) {
-            break;
-          }
-          close(inf._fd);
-          inf._fd = -1;
-          break;
-        }
-      }
-    }
-
-    // Process events.
-    int nevents = inf._io_buffer.size() / sizeof(struct input_event);
-    if (nevents == 0) {
-      continue;
-    }
-    const input_event *events = (const input_event *)(inf._io_buffer.c_str());
-    GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index];
-    int x = dev.get_raw_pointer().get_x();
-    int y = dev.get_raw_pointer().get_y();
-    for (int i=0; i<nevents; i++) {
-      if (events[i].type == EV_REL) {
-        if (events[i].code == REL_X) x += events[i].value;
-        if (events[i].code == REL_Y) y += events[i].value;
-      } else if (events[i].type == EV_ABS) {
-        if (events[i].code == ABS_X) x = events[i].value;
-        if (events[i].code == ABS_Y) y = events[i].value;
-      } else if (events[i].type == EV_KEY) {
-        if ((events[i].code >= BTN_MOUSE)&&(events[i].code < BTN_MOUSE+8)) {
-          int btn = events[i].code - BTN_MOUSE;
-          dev.set_pointer_in_window(x,y);
-          if (events[i].value) {
-            dev.button_down(MouseButton::button(btn));
-          } else {
-            dev.button_up(MouseButton::button(btn));
-          }
-        }
-      }
-    }
-    inf._io_buffer.erase(0,nevents*sizeof(struct input_event));
-    dev.set_pointer_in_window(x,y);
-  }
-#endif
-}
-
-/**
- * Generates a keystroke corresponding to the indicated X KeyPress event.
- */
-void eglGraphicsWindow::
-handle_keystroke(XKeyEvent &event) {
-  _input_devices[0].set_pointer_in_window(event.x, event.y);
-
-  if (_ic) {
-    // First, get the keystroke as a wide-character sequence.
-    static const int buffer_size = 256;
-    wchar_t buffer[buffer_size];
-    Status status;
-    int len = XwcLookupString(_ic, &event, buffer, buffer_size, NULL,
-                              &status);
-    if (status == XBufferOverflow) {
-      egldisplay_cat.error()
-        << "Overflowed input buffer.\n";
-    }
-
-    // Now each of the returned wide characters represents a keystroke.
-    for (int i = 0; i < len; i++) {
-      _input_devices[0].keystroke(buffer[i]);
-    }
-
-  } else {
-    // Without an input context, just get the ascii keypress.
-    ButtonHandle button = get_button(event, true);
-    if (button.has_ascii_equivalent()) {
-      _input_devices[0].keystroke(button.get_ascii_equivalent());
-    }
-  }
-}
-
-/**
- * Generates a keypress corresponding to the indicated X KeyPress event.
- */
-void eglGraphicsWindow::
-handle_keypress(XKeyEvent &event) {
-  _input_devices[0].set_pointer_in_window(event.x, event.y);
-
-  // Now get the raw unshifted button.
-  ButtonHandle button = get_button(event, false);
-  if (button != ButtonHandle::none()) {
-    if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
-      _input_devices[0].button_down(KeyboardButton::control());
-    }
-    if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
-      _input_devices[0].button_down(KeyboardButton::shift());
-    }
-    if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
-      _input_devices[0].button_down(KeyboardButton::alt());
-    }
-    if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
-      _input_devices[0].button_down(KeyboardButton::meta());
-    }
-    _input_devices[0].button_down(button);
-  }
-}
-
-/**
- * Generates a keyrelease corresponding to the indicated X KeyRelease event.
- */
-void eglGraphicsWindow::
-handle_keyrelease(XKeyEvent &event) {
-  _input_devices[0].set_pointer_in_window(event.x, event.y);
-
-  // Now get the raw unshifted button.
-  ButtonHandle button = get_button(event, false);
-  if (button != ButtonHandle::none()) {
-    if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
-      _input_devices[0].button_up(KeyboardButton::control());
-    }
-    if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
-      _input_devices[0].button_up(KeyboardButton::shift());
-    }
-    if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
-      _input_devices[0].button_up(KeyboardButton::alt());
-    }
-    if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
-      _input_devices[0].button_up(KeyboardButton::meta());
-    }
-    _input_devices[0].button_up(button);
-  }
-}
-
-/**
- * Returns the Panda ButtonHandle corresponding to the keyboard button
- * indicated by the given key event.
- */
-ButtonHandle eglGraphicsWindow::
-get_button(XKeyEvent &key_event, bool allow_shift) {
-  KeySym key = XLookupKeysym(&key_event, 0);
-
-  if ((key_event.state & Mod2Mask) != 0) {
-    // Mod2Mask corresponds to NumLock being in effect.  In this case, we want
-    // to get the alternate keysym associated with any keypad keys.  Weird
-    // system.
-    KeySym k2;
-    ButtonHandle button;
-    switch (key) {
-    case XK_KP_Space:
-    case XK_KP_Tab:
-    case XK_KP_Enter:
-    case XK_KP_F1:
-    case XK_KP_F2:
-    case XK_KP_F3:
-    case XK_KP_F4:
-    case XK_KP_Equal:
-    case XK_KP_Multiply:
-    case XK_KP_Add:
-    case XK_KP_Separator:
-    case XK_KP_Subtract:
-    case XK_KP_Divide:
-    case XK_KP_Left:
-    case XK_KP_Up:
-    case XK_KP_Right:
-    case XK_KP_Down:
-    case XK_KP_Begin:
-    case XK_KP_Prior:
-    case XK_KP_Next:
-    case XK_KP_Home:
-    case XK_KP_End:
-    case XK_KP_Insert:
-    case XK_KP_Delete:
-    case XK_KP_0:
-    case XK_KP_1:
-    case XK_KP_2:
-    case XK_KP_3:
-    case XK_KP_4:
-    case XK_KP_5:
-    case XK_KP_6:
-    case XK_KP_7:
-    case XK_KP_8:
-    case XK_KP_9:
-      k2 = XLookupKeysym(&key_event, 1);
-      button = map_button(k2);
-      if (button != ButtonHandle::none()) {
-        return button;
-      }
-      // If that didn't produce a button we know, just fall through and handle
-      // the normal, un-numlocked key.
-      break;
-
-    default:
-      break;
-    }
-  }
-
-  if (allow_shift) {
-    // If shift is held down, get the shifted keysym.
-    if ((key_event.state & ShiftMask) != 0) {
-      KeySym k2 = XLookupKeysym(&key_event, 1);
-      ButtonHandle button = map_button(k2);
-      if (button != ButtonHandle::none()) {
-        return button;
-      }
-    }
-
-    // If caps lock is down, shift lowercase letters to uppercase.  We can do
-    // this in just the ASCII set, because we handle international keyboards
-    // elsewhere (via an input context).
-    if ((key_event.state & (ShiftMask | LockMask)) != 0) {
-      if (key >= XK_a and key <= XK_z) {
-        key += (XK_A - XK_a);
-      }
-    }
-  }
-
-  return map_button(key);
-}
-
-/**
- * Maps from a single X keysym to Panda's ButtonHandle.  Called by
- * get_button(), above.
- */
-ButtonHandle eglGraphicsWindow::
-map_button(KeySym key) {
-  switch (key) {
-  case XK_BackSpace:
-    return KeyboardButton::backspace();
-  case XK_Tab:
-  case XK_KP_Tab:
-    return KeyboardButton::tab();
-  case XK_Return:
-  case XK_KP_Enter:
-    return KeyboardButton::enter();
-  case XK_Escape:
-    return KeyboardButton::escape();
-  case XK_KP_Space:
-  case XK_space:
-    return KeyboardButton::space();
-  case XK_exclam:
-    return KeyboardButton::ascii_key('!');
-  case XK_quotedbl:
-    return KeyboardButton::ascii_key('"');
-  case XK_numbersign:
-    return KeyboardButton::ascii_key('#');
-  case XK_dollar:
-    return KeyboardButton::ascii_key('$');
-  case XK_percent:
-    return KeyboardButton::ascii_key('%');
-  case XK_ampersand:
-    return KeyboardButton::ascii_key('&');
-  case XK_apostrophe: // == XK_quoteright
-    return KeyboardButton::ascii_key('\'');
-  case XK_parenleft:
-    return KeyboardButton::ascii_key('(');
-  case XK_parenright:
-    return KeyboardButton::ascii_key(')');
-  case XK_asterisk:
-  case XK_KP_Multiply:
-    return KeyboardButton::ascii_key('*');
-  case XK_plus:
-  case XK_KP_Add:
-    return KeyboardButton::ascii_key('+');
-  case XK_comma:
-  case XK_KP_Separator:
-    return KeyboardButton::ascii_key(',');
-  case XK_minus:
-  case XK_KP_Subtract:
-    return KeyboardButton::ascii_key('-');
-  case XK_period:
-  case XK_KP_Decimal:
-    return KeyboardButton::ascii_key('.');
-  case XK_slash:
-  case XK_KP_Divide:
-    return KeyboardButton::ascii_key('/');
-  case XK_0:
-  case XK_KP_0:
-    return KeyboardButton::ascii_key('0');
-  case XK_1:
-  case XK_KP_1:
-    return KeyboardButton::ascii_key('1');
-  case XK_2:
-  case XK_KP_2:
-    return KeyboardButton::ascii_key('2');
-  case XK_3:
-  case XK_KP_3:
-    return KeyboardButton::ascii_key('3');
-  case XK_4:
-  case XK_KP_4:
-    return KeyboardButton::ascii_key('4');
-  case XK_5:
-  case XK_KP_5:
-    return KeyboardButton::ascii_key('5');
-  case XK_6:
-  case XK_KP_6:
-    return KeyboardButton::ascii_key('6');
-  case XK_7:
-  case XK_KP_7:
-    return KeyboardButton::ascii_key('7');
-  case XK_8:
-  case XK_KP_8:
-    return KeyboardButton::ascii_key('8');
-  case XK_9:
-  case XK_KP_9:
-    return KeyboardButton::ascii_key('9');
-  case XK_colon:
-    return KeyboardButton::ascii_key(':');
-  case XK_semicolon:
-    return KeyboardButton::ascii_key(';');
-  case XK_less:
-    return KeyboardButton::ascii_key('<');
-  case XK_equal:
-  case XK_KP_Equal:
-    return KeyboardButton::ascii_key('=');
-  case XK_greater:
-    return KeyboardButton::ascii_key('>');
-  case XK_question:
-    return KeyboardButton::ascii_key('?');
-  case XK_at:
-    return KeyboardButton::ascii_key('@');
-  case XK_A:
-    return KeyboardButton::ascii_key('A');
-  case XK_B:
-    return KeyboardButton::ascii_key('B');
-  case XK_C:
-    return KeyboardButton::ascii_key('C');
-  case XK_D:
-    return KeyboardButton::ascii_key('D');
-  case XK_E:
-    return KeyboardButton::ascii_key('E');
-  case XK_F:
-    return KeyboardButton::ascii_key('F');
-  case XK_G:
-    return KeyboardButton::ascii_key('G');
-  case XK_H:
-    return KeyboardButton::ascii_key('H');
-  case XK_I:
-    return KeyboardButton::ascii_key('I');
-  case XK_J:
-    return KeyboardButton::ascii_key('J');
-  case XK_K:
-    return KeyboardButton::ascii_key('K');
-  case XK_L:
-    return KeyboardButton::ascii_key('L');
-  case XK_M:
-    return KeyboardButton::ascii_key('M');
-  case XK_N:
-    return KeyboardButton::ascii_key('N');
-  case XK_O:
-    return KeyboardButton::ascii_key('O');
-  case XK_P:
-    return KeyboardButton::ascii_key('P');
-  case XK_Q:
-    return KeyboardButton::ascii_key('Q');
-  case XK_R:
-    return KeyboardButton::ascii_key('R');
-  case XK_S:
-    return KeyboardButton::ascii_key('S');
-  case XK_T:
-    return KeyboardButton::ascii_key('T');
-  case XK_U:
-    return KeyboardButton::ascii_key('U');
-  case XK_V:
-    return KeyboardButton::ascii_key('V');
-  case XK_W:
-    return KeyboardButton::ascii_key('W');
-  case XK_X:
-    return KeyboardButton::ascii_key('X');
-  case XK_Y:
-    return KeyboardButton::ascii_key('Y');
-  case XK_Z:
-    return KeyboardButton::ascii_key('Z');
-  case XK_bracketleft:
-    return KeyboardButton::ascii_key('[');
-  case XK_backslash:
-    return KeyboardButton::ascii_key('\\');
-  case XK_bracketright:
-    return KeyboardButton::ascii_key(']');
-  case XK_asciicircum:
-    return KeyboardButton::ascii_key('^');
-  case XK_underscore:
-    return KeyboardButton::ascii_key('_');
-  case XK_grave: // == XK_quoteleft
-    return KeyboardButton::ascii_key('`');
-  case XK_a:
-    return KeyboardButton::ascii_key('a');
-  case XK_b:
-    return KeyboardButton::ascii_key('b');
-  case XK_c:
-    return KeyboardButton::ascii_key('c');
-  case XK_d:
-    return KeyboardButton::ascii_key('d');
-  case XK_e:
-    return KeyboardButton::ascii_key('e');
-  case XK_f:
-    return KeyboardButton::ascii_key('f');
-  case XK_g:
-    return KeyboardButton::ascii_key('g');
-  case XK_h:
-    return KeyboardButton::ascii_key('h');
-  case XK_i:
-    return KeyboardButton::ascii_key('i');
-  case XK_j:
-    return KeyboardButton::ascii_key('j');
-  case XK_k:
-    return KeyboardButton::ascii_key('k');
-  case XK_l:
-    return KeyboardButton::ascii_key('l');
-  case XK_m:
-    return KeyboardButton::ascii_key('m');
-  case XK_n:
-    return KeyboardButton::ascii_key('n');
-  case XK_o:
-    return KeyboardButton::ascii_key('o');
-  case XK_p:
-    return KeyboardButton::ascii_key('p');
-  case XK_q:
-    return KeyboardButton::ascii_key('q');
-  case XK_r:
-    return KeyboardButton::ascii_key('r');
-  case XK_s:
-    return KeyboardButton::ascii_key('s');
-  case XK_t:
-    return KeyboardButton::ascii_key('t');
-  case XK_u:
-    return KeyboardButton::ascii_key('u');
-  case XK_v:
-    return KeyboardButton::ascii_key('v');
-  case XK_w:
-    return KeyboardButton::ascii_key('w');
-  case XK_x:
-    return KeyboardButton::ascii_key('x');
-  case XK_y:
-    return KeyboardButton::ascii_key('y');
-  case XK_z:
-    return KeyboardButton::ascii_key('z');
-  case XK_braceleft:
-    return KeyboardButton::ascii_key('{');
-  case XK_bar:
-    return KeyboardButton::ascii_key('|');
-  case XK_braceright:
-    return KeyboardButton::ascii_key('}');
-  case XK_asciitilde:
-    return KeyboardButton::ascii_key('~');
-  case XK_F1:
-  case XK_KP_F1:
-    return KeyboardButton::f1();
-  case XK_F2:
-  case XK_KP_F2:
-    return KeyboardButton::f2();
-  case XK_F3:
-  case XK_KP_F3:
-    return KeyboardButton::f3();
-  case XK_F4:
-  case XK_KP_F4:
-    return KeyboardButton::f4();
-  case XK_F5:
-    return KeyboardButton::f5();
-  case XK_F6:
-    return KeyboardButton::f6();
-  case XK_F7:
-    return KeyboardButton::f7();
-  case XK_F8:
-    return KeyboardButton::f8();
-  case XK_F9:
-    return KeyboardButton::f9();
-  case XK_F10:
-    return KeyboardButton::f10();
-  case XK_F11:
-    return KeyboardButton::f11();
-  case XK_F12:
-    return KeyboardButton::f12();
-  case XK_KP_Left:
-  case XK_Left:
-    return KeyboardButton::left();
-  case XK_KP_Up:
-  case XK_Up:
-    return KeyboardButton::up();
-  case XK_KP_Right:
-  case XK_Right:
-    return KeyboardButton::right();
-  case XK_KP_Down:
-  case XK_Down:
-    return KeyboardButton::down();
-  case XK_KP_Prior:
-  case XK_Prior:
-    return KeyboardButton::page_up();
-  case XK_KP_Next:
-  case XK_Next:
-    return KeyboardButton::page_down();
-  case XK_KP_Home:
-  case XK_Home:
-    return KeyboardButton::home();
-  case XK_KP_End:
-  case XK_End:
-    return KeyboardButton::end();
-  case XK_KP_Insert:
-  case XK_Insert:
-    return KeyboardButton::insert();
-  case XK_KP_Delete:
-  case XK_Delete:
-    return KeyboardButton::del();
-  case XK_Num_Lock:
-    return KeyboardButton::num_lock();
-  case XK_Scroll_Lock:
-    return KeyboardButton::scroll_lock();
-  case XK_Print:
-    return KeyboardButton::print_screen();
-  case XK_Pause:
-    return KeyboardButton::pause();
-  case XK_Menu:
-    return KeyboardButton::menu();
-  case XK_Shift_L:
-    return KeyboardButton::lshift();
-  case XK_Shift_R:
-    return KeyboardButton::rshift();
-  case XK_Control_L:
-    return KeyboardButton::lcontrol();
-  case XK_Control_R:
-    return KeyboardButton::rcontrol();
-  case XK_Alt_L:
-    return KeyboardButton::lalt();
-  case XK_Alt_R:
-    return KeyboardButton::ralt();
-  case XK_Meta_L:
-    return KeyboardButton::lmeta();
-  case XK_Meta_R:
-    return KeyboardButton::rmeta();
-  case XK_Caps_Lock:
-    return KeyboardButton::caps_lock();
-  case XK_Shift_Lock:
-    return KeyboardButton::shift_lock();
-  }
-
-  return ButtonHandle::none();
-}
-
-/**
- * Returns the Panda ButtonHandle corresponding to the mouse button indicated
- * by the given button event.
- */
-ButtonHandle eglGraphicsWindow::
-get_mouse_button(XButtonEvent &button_event) {
-  int index = button_event.button;
-  if (index == x_wheel_up_button) {
-    return MouseButton::wheel_up();
-  } else if (index == x_wheel_down_button) {
-    return MouseButton::wheel_down();
-  } else if (index == x_wheel_left_button) {
-    return MouseButton::wheel_left();
-  } else if (index == x_wheel_right_button) {
-    return MouseButton::wheel_right();
-  } else {
-    return MouseButton::button(index - 1);
-  }
-}
-/**
- * This function is used as a predicate to XCheckIfEvent() to determine if the
- * indicated queued X event is relevant and should be returned to this window.
- */
-Bool eglGraphicsWindow::
-check_event(X11_Display *display, XEvent *event, char *arg) {
-  const eglGraphicsWindow *self = (eglGraphicsWindow *)arg;
-
-  // We accept any event that is sent to our window.
-  return (event->xany.window == self->_xwindow);
-}

+ 4 - 54
panda/src/egldisplay/eglGraphicsWindow.h

@@ -17,14 +17,12 @@
 #include "pandabase.h"
 #include "pandabase.h"
 
 
 #include "eglGraphicsPipe.h"
 #include "eglGraphicsPipe.h"
-#include "graphicsWindow.h"
-#include "buttonHandle.h"
-#include "get_x11.h"
+#include "x11GraphicsWindow.h"
 
 
 /**
 /**
  * An interface to the egl system for managing GLES windows under X.
  * An interface to the egl system for managing GLES windows under X.
  */
  */
-class eglGraphicsWindow : public GraphicsWindow {
+class eglGraphicsWindow : public x11GraphicsWindow {
 public:
 public:
   eglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
   eglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
                     const string &name,
                     const string &name,
@@ -40,70 +38,22 @@ public:
   virtual void end_frame(FrameMode mode, Thread *current_thread);
   virtual void end_frame(FrameMode mode, Thread *current_thread);
   virtual void end_flip();
   virtual void end_flip();
 
 
-  virtual void process_events();
-  virtual void set_properties_now(WindowProperties &properties);
-
-  INLINE X11_Window get_xwindow() const;
-
 protected:
 protected:
   virtual void close_window();
   virtual void close_window();
   virtual bool open_window();
   virtual bool open_window();
 
 
 private:
 private:
-  void set_wm_properties(const WindowProperties &properties,
-                         bool already_mapped);
-
-  void setup_colormap(XVisualInfo *visual);
-  void handle_keystroke(XKeyEvent &event);
-  void handle_keypress(XKeyEvent &event);
-  void handle_keyrelease(XKeyEvent &event);
-
-  ButtonHandle get_button(XKeyEvent &key_event, bool allow_shift);
-  ButtonHandle map_button(KeySym key);
-  ButtonHandle get_mouse_button(XButtonEvent &button_event);
-
-  static Bool check_event(X11_Display *display, XEvent *event, char *arg);
-
-  void open_raw_mice();
-  void poll_raw_mice();
-
-private:
-  X11_Display *_display;
-  int _screen;
-  X11_Window _xwindow;
-  Colormap _colormap;
-  XIC _ic;
   EGLDisplay _egl_display;
   EGLDisplay _egl_display;
   EGLSurface _egl_surface;
   EGLSurface _egl_surface;
 
 
-  long _event_mask;
-  bool _awaiting_configure;
-  Atom _wm_delete_window;
-  Atom _net_wm_window_type;
-  Atom _net_wm_window_type_splash;
-  Atom _net_wm_window_type_fullscreen;
-  Atom _net_wm_state;
-  Atom _net_wm_state_fullscreen;
-  Atom _net_wm_state_above;
-  Atom _net_wm_state_below;
-  Atom _net_wm_state_add;
-  Atom _net_wm_state_remove;
-
-  struct MouseDeviceInfo {
-    int    _fd;
-    int    _input_device_index;
-    string _io_buffer;
-  };
-  pvector<MouseDeviceInfo> _mouse_device_info;
-
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    GraphicsWindow::init_type();
+    x11GraphicsWindow::init_type();
     register_type(_type_handle, "eglGraphicsWindow",
     register_type(_type_handle, "eglGraphicsWindow",
-                  GraphicsWindow::get_class_type());
+                  x11GraphicsWindow::get_class_type());
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
     return get_class_type();
     return get_class_type();

+ 10 - 2
panda/src/express/virtualFileMountRamdisk.cxx

@@ -235,7 +235,13 @@ open_write_file(const Filename &file, bool truncate) {
   if (truncate) {
   if (truncate) {
     // Reset to an empty string.
     // Reset to an empty string.
     f->_data.str(string());
     f->_data.str(string());
-    f->_timestamp = time(NULL);
+
+    // Instead of setting the time, we ensure that we always store a newer time.
+    // This is a workarround for the case that a file is written twice per
+    // second, since the timer only has a one second precision. The proper
+    // solution to fix this would be to switch to a higher precision
+    // timer everywhere.
+    f->_timestamp = max(f->_timestamp + 1, time(NULL));
   }
   }
 
 
   return new OSubStream(&f->_wrapper, 0, 0);
   return new OSubStream(&f->_wrapper, 0, 0);
@@ -275,7 +281,9 @@ open_read_write_file(const Filename &file, bool truncate) {
   if (truncate) {
   if (truncate) {
     // Reset to an empty string.
     // Reset to an empty string.
     f->_data.str(string());
     f->_data.str(string());
-    f->_timestamp = time(NULL);
+
+    // See open_write_file
+    f->_timestamp = max(f->_timestamp + 1, time(NULL));
   }
   }
 
 
   return new SubStream(&f->_wrapper, 0, 0);
   return new SubStream(&f->_wrapper, 0, 0);

+ 2 - 0
panda/src/framework/config_framework.cxx

@@ -31,6 +31,8 @@ ConfigVariableBool show_frame_rate_meter
 ("show-frame-rate-meter", false);
 ("show-frame-rate-meter", false);
 ConfigVariableBool show_scene_graph_analyzer_meter
 ConfigVariableBool show_scene_graph_analyzer_meter
 ("show-scene-graph-analyzer-meter", false);
 ("show-scene-graph-analyzer-meter", false);
+ConfigVariableBool print_pipe_types
+("print-pipe-types", true);
 ConfigVariableString window_type
 ConfigVariableString window_type
 ("window-type", "onscreen");
 ("window-type", "onscreen");
 
 

+ 1 - 0
panda/src/framework/config_framework.h

@@ -27,6 +27,7 @@ NotifyCategoryDecl(framework, EXPCL_FRAMEWORK, EXPTP_FRAMEWORK);
 extern ConfigVariableDouble aspect_ratio;
 extern ConfigVariableDouble aspect_ratio;
 extern ConfigVariableBool show_frame_rate_meter;
 extern ConfigVariableBool show_frame_rate_meter;
 extern ConfigVariableBool show_scene_graph_analyzer_meter;
 extern ConfigVariableBool show_scene_graph_analyzer_meter;
+extern ConfigVariableBool print_pipe_types;
 extern ConfigVariableString window_type;
 extern ConfigVariableString window_type;
 
 
 extern ConfigVariableString record_session;
 extern ConfigVariableString record_session;

+ 5 - 1
panda/src/framework/pandaFramework.cxx

@@ -789,7 +789,11 @@ make_default_pipe() {
   // folks) that have been loaded in at runtime from the load-display andor
   // folks) that have been loaded in at runtime from the load-display andor
   // aux-display Configrc variables.
   // aux-display Configrc variables.
   GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
   GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
-  selection->print_pipe_types();
+
+  if (print_pipe_types) {
+    selection->print_pipe_types();
+  }
+
   _default_pipe = selection->make_default_pipe();
   _default_pipe = selection->make_default_pipe();
 
 
   if (_default_pipe == (GraphicsPipe*)NULL) {
   if (_default_pipe == (GraphicsPipe*)NULL) {

+ 23 - 6
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -509,6 +509,7 @@ rebuild_bitplanes() {
 
 
   // Now create the FBO's.
   // Now create the FBO's.
   _have_any_color = false;
   _have_any_color = false;
+  bool have_any_depth = false;
 
 
   if (num_fbos > _fbo.size()) {
   if (num_fbos > _fbo.size()) {
     // Generate more FBO handles.
     // Generate more FBO handles.
@@ -542,8 +543,10 @@ rebuild_bitplanes() {
     // For all slots, update the slot.
     // For all slots, update the slot.
     if (_use_depth_stencil) {
     if (_use_depth_stencil) {
       bind_slot(layer, rb_resize, attach, RTP_depth_stencil, GL_DEPTH_ATTACHMENT_EXT);
       bind_slot(layer, rb_resize, attach, RTP_depth_stencil, GL_DEPTH_ATTACHMENT_EXT);
+      have_any_depth = true;
     } else if (attach[RTP_depth] || _fb_properties.get_depth_bits() > 0) {
     } else if (attach[RTP_depth] || _fb_properties.get_depth_bits() > 0) {
       bind_slot(layer, rb_resize, attach, RTP_depth, GL_DEPTH_ATTACHMENT_EXT);
       bind_slot(layer, rb_resize, attach, RTP_depth, GL_DEPTH_ATTACHMENT_EXT);
+      have_any_depth = true;
     }
     }
 
 
     int next = GL_COLOR_ATTACHMENT0_EXT;
     int next = GL_COLOR_ATTACHMENT0_EXT;
@@ -581,11 +584,23 @@ rebuild_bitplanes() {
     }
     }
 #endif  // OPENGLES
 #endif  // OPENGLES
 
 
-    // Clear if the fbo was just created, regardless of the clear settings per
-    // frame.
-    if (_initial_clear) {
-      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+    if (_have_any_color || have_any_depth) {
+      // Clear if the fbo was just created, regardless of the clear settings per
+      // frame.
+      if (_initial_clear) {
+        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+      }
+#ifndef OPENGLES
+    } else if (glgsg->_supports_empty_framebuffer) {
+      // Set the "default" width and height, which is required to have an FBO
+      // without any attachments.
+      glgsg->_glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, _rb_size_x);
+      glgsg->_glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, _rb_size_y);
+#endif
+    } else {
+      // If all else fails, just bind a "dummy" attachment.
+      bind_slot(layer, rb_resize, attach, RTP_color, next++);
     }
     }
   }
   }
 
 
@@ -1363,8 +1378,10 @@ open_buffer() {
     _fb_properties.set_depth_bits(32);
     _fb_properties.set_depth_bits(32);
   } else if (_fb_properties.get_depth_bits() > 16) {
   } else if (_fb_properties.get_depth_bits() > 16) {
     _fb_properties.set_depth_bits(24);
     _fb_properties.set_depth_bits(24);
-  } else {
+  } else if (_fb_properties.get_depth_bits() > 0) {
     _fb_properties.set_depth_bits(16);
     _fb_properties.set_depth_bits(16);
+  } else {
+    _fb_properties.set_depth_bits(0);
   }
   }
 
 
   // We're not going to get more than this, ever.  At least not until OpenGL
   // We're not going to get more than this, ever.  At least not until OpenGL

+ 37 - 6
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -169,7 +169,7 @@ static const string default_fshader =
   "#version 130\n"
   "#version 130\n"
   "in vec2 texcoord;\n"
   "in vec2 texcoord;\n"
   "in vec4 color;\n"
   "in vec4 color;\n"
-  "out vec4 p3d_FragColor;"
+  "out vec4 p3d_FragColor;\n"
   "uniform sampler2D p3d_Texture0;\n"
   "uniform sampler2D p3d_Texture0;\n"
   "uniform vec4 p3d_TexAlphaOnly;\n"
   "uniform vec4 p3d_TexAlphaOnly;\n"
 #else
 #else
@@ -414,7 +414,12 @@ debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei l
     break;
     break;
 
 
   case GL_DEBUG_SEVERITY_MEDIUM:
   case GL_DEBUG_SEVERITY_MEDIUM:
-    level = NS_warning;
+    if (type == GL_DEBUG_TYPE_PERFORMANCE) {
+      // Performance warnings should really be "info".
+      level = NS_info;
+    } else {
+      level = NS_warning;
+    }
     break;
     break;
 
 
   case GL_DEBUG_SEVERITY_LOW:
   case GL_DEBUG_SEVERITY_LOW:
@@ -1947,6 +1952,16 @@ reset() {
   }
   }
 #endif
 #endif
 
 
+#ifndef OPENGLES
+  if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_framebuffer_no_attachments")) {
+    _glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC)
+      get_extension_func("glFramebufferParameteri");
+    _supports_empty_framebuffer = true;
+  } else {
+    _supports_empty_framebuffer = false;
+  }
+#endif
+
   _supports_framebuffer_multisample = false;
   _supports_framebuffer_multisample = false;
   if (has_extension("GL_EXT_framebuffer_multisample")) {
   if (has_extension("GL_EXT_framebuffer_multisample")) {
     _supports_framebuffer_multisample = true;
     _supports_framebuffer_multisample = true;
@@ -2527,7 +2542,7 @@ reset() {
 
 
   if (core_profile) {
   if (core_profile) {
     // TODO: better detection mechanism?
     // TODO: better detection mechanism?
-    _supports_stencil = true;
+    _supports_stencil = support_stencil;
   }
   }
 #ifdef SUPPORT_FIXED_FUNCTION
 #ifdef SUPPORT_FIXED_FUNCTION
   else if (support_stencil) {
   else if (support_stencil) {
@@ -2998,7 +3013,7 @@ clear(DrawableRegion *clearable) {
     mask |= GL_DEPTH_BUFFER_BIT;
     mask |= GL_DEPTH_BUFFER_BIT;
   }
   }
 
 
-  if (clearable->get_clear_stencil_active()) {
+  if (_supports_stencil && clearable->get_clear_stencil_active()) {
     glStencilMask(~0);
     glStencilMask(~0);
     glClearStencil(clearable->get_clear_stencil());
     glClearStencil(clearable->get_clear_stencil());
     mask |= GL_STENCIL_BUFFER_BIT;
     mask |= GL_STENCIL_BUFFER_BIT;
@@ -4826,6 +4841,7 @@ update_texture(TextureContext *tc, bool force) {
     if (gtc->was_properties_modified()) {
     if (gtc->was_properties_modified()) {
       specify_texture(gtc, tex->get_default_sampler());
       specify_texture(gtc, tex->get_default_sampler());
     }
     }
+
     bool okflag = upload_texture(gtc, force, tex->uses_mipmaps());
     bool okflag = upload_texture(gtc, force, tex->uses_mipmaps());
     if (!okflag) {
     if (!okflag) {
       GLCAT.error()
       GLCAT.error()
@@ -6247,7 +6263,9 @@ do_issue_render_mode() {
   }
   }
   report_my_gl_errors();
   report_my_gl_errors();
 
 
+#ifdef SUPPORT_FIXED_FUNCTION
   do_point_size();
   do_point_size();
+#endif
 }
 }
 
 
 /**
 /**
@@ -6728,6 +6746,19 @@ do_issue_blending() {
     }
     }
     return;
     return;
 
 
+  case TransparencyAttrib::M_premultiplied_alpha:
+    enable_multisample_alpha_one(false);
+    enable_multisample_alpha_mask(false);
+    enable_blend(true);
+    _glBlendEquation(GL_FUNC_ADD);
+    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+    if (GLCAT.is_spam()) {
+      GLCAT.spam() << "glBlendEquation(GL_FUNC_ADD)\n";
+      GLCAT.spam() << "glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)\n";
+    }
+    return;
+
   case TransparencyAttrib::M_multisample:
   case TransparencyAttrib::M_multisample:
     // We need to enable *both* of these in M_multisample case.
     // We need to enable *both* of these in M_multisample case.
     enable_multisample_alpha_one(true);
     enable_multisample_alpha_one(true);
@@ -12721,9 +12752,9 @@ extract_texture_image(PTA_uchar &image, size_t &page_size,
  * Internally sets the point size parameters after any of the properties have
  * Internally sets the point size parameters after any of the properties have
  * changed that might affect this.
  * changed that might affect this.
  */
  */
+#ifdef SUPPORT_FIXED_FUNCTION
 void CLP(GraphicsStateGuardian)::
 void CLP(GraphicsStateGuardian)::
 do_point_size() {
 do_point_size() {
-#ifndef OPENGLES_2
   if (!_point_perspective) {
   if (!_point_perspective) {
     // Normal, constant-sized points.  Here _point_size is a width in pixels.
     // Normal, constant-sized points.  Here _point_size is a width in pixels.
     static LVecBase3f constant(1.0f, 0.0f, 0.0f);
     static LVecBase3f constant(1.0f, 0.0f, 0.0f);
@@ -12753,8 +12784,8 @@ do_point_size() {
   }
   }
 
 
   report_my_gl_errors();
   report_my_gl_errors();
-#endif
 }
 }
+#endif
 
 
 /**
 /**
  * Returns true if this particular GSG supports the specified Cg Shader
  * Returns true if this particular GSG supports the specified Cg Shader

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

@@ -567,7 +567,9 @@ protected:
            Texture::ComponentType type,
            Texture::ComponentType type,
            Texture::CompressionMode compression, int n);
            Texture::CompressionMode compression, int n);
 
 
+#ifdef SUPPORT_FIXED_FUNCTION
   void do_point_size();
   void do_point_size();
+#endif
 
 
   enum AutoAntialiasMode {
   enum AutoAntialiasMode {
     AA_poly,
     AA_poly,
@@ -828,6 +830,11 @@ public:
   PFNGLGENERATETEXTUREMIPMAPPROC _glGenerateTextureMipmap;
   PFNGLGENERATETEXTUREMIPMAPPROC _glGenerateTextureMipmap;
 #endif
 #endif
 
 
+#ifndef OPENGLES
+  bool _supports_empty_framebuffer;
+  PFNGLFRAMEBUFFERPARAMETERIPROC _glFramebufferParameteri;
+#endif
+
   bool _supports_framebuffer_multisample;
   bool _supports_framebuffer_multisample;
   bool _supports_framebuffer_multisample_coverage_nv;
   bool _supports_framebuffer_multisample_coverage_nv;
   INLINE bool get_supports_framebuffer_multisample();
   INLINE bool get_supports_framebuffer_multisample();

+ 22 - 19
panda/src/glstuff/glShaderContext_src.cxx

@@ -594,7 +594,7 @@ reflect_uniform_block(int i, const char *name, char *name_buffer, GLsizei name_b
       break;
       break;
 
 
     default:
     default:
-      GLCAT.warning() << "Ignoring uniform '" << name_buffer
+      GLCAT.info() << "Ignoring uniform '" << name_buffer
         << "' with unsupported type 0x" << hex << param_type << dec << "\n";
         << "' with unsupported type 0x" << hex << param_type << dec << "\n";
       continue;
       continue;
     }
     }
@@ -1462,24 +1462,27 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
         return;
         return;
       }
       }
 #ifndef OPENGLES
 #ifndef OPENGLES
-      case GL_IMAGE_1D_EXT:
-      case GL_IMAGE_2D_EXT:
-      case GL_IMAGE_3D_EXT:
-      case GL_IMAGE_CUBE_EXT:
-      case GL_IMAGE_2D_ARRAY_EXT:
-      case GL_IMAGE_BUFFER_EXT:
-      case GL_INT_IMAGE_1D_EXT:
-      case GL_INT_IMAGE_2D_EXT:
-      case GL_INT_IMAGE_3D_EXT:
-      case GL_INT_IMAGE_CUBE_EXT:
-      case GL_INT_IMAGE_2D_ARRAY_EXT:
-      case GL_INT_IMAGE_BUFFER_EXT:
-      case GL_UNSIGNED_INT_IMAGE_1D_EXT:
-      case GL_UNSIGNED_INT_IMAGE_2D_EXT:
-      case GL_UNSIGNED_INT_IMAGE_3D_EXT:
-      case GL_UNSIGNED_INT_IMAGE_CUBE_EXT:
-      case GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT:
-      case GL_UNSIGNED_INT_IMAGE_BUFFER_EXT:
+      case GL_IMAGE_1D:
+      case GL_IMAGE_2D:
+      case GL_IMAGE_3D:
+      case GL_IMAGE_CUBE:
+      case GL_IMAGE_2D_ARRAY:
+      case GL_IMAGE_CUBE_MAP_ARRAY:
+      case GL_IMAGE_BUFFER:
+      case GL_INT_IMAGE_1D:
+      case GL_INT_IMAGE_2D:
+      case GL_INT_IMAGE_3D:
+      case GL_INT_IMAGE_CUBE:
+      case GL_INT_IMAGE_2D_ARRAY:
+      case GL_INT_IMAGE_CUBE_MAP_ARRAY:
+      case GL_INT_IMAGE_BUFFER:
+      case GL_UNSIGNED_INT_IMAGE_1D:
+      case GL_UNSIGNED_INT_IMAGE_2D:
+      case GL_UNSIGNED_INT_IMAGE_3D:
+      case GL_UNSIGNED_INT_IMAGE_CUBE:
+      case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+      case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
+      case GL_UNSIGNED_INT_IMAGE_BUFFER:
         // This won't really change at runtime, so we might as well bind once
         // This won't really change at runtime, so we might as well bind once
         // and then forget about it.
         // and then forget about it.
         _glgsg->_glUniform1i(p, _glsl_img_inputs.size());
         _glgsg->_glUniform1i(p, _glsl_img_inputs.size());

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

@@ -112,13 +112,6 @@ ConfigVariableBool keep_texture_ram
           "texture image from disk; but it will consume memory somewhat "
           "texture image from disk; but it will consume memory somewhat "
           "wastefully."));
           "wastefully."));
 
 
-ConfigVariableBool compressed_textures
-("compressed-textures", false,
- PRC_DESC("Set this to true to compress textures as they are loaded into "
-          "texture memory, if the driver supports this.  Specifically, this "
-          "changes the meaning of set_compression(Texture::CM_default) to "
-          "Texture::CM_on."));
-
 ConfigVariableBool driver_compress_textures
 ConfigVariableBool driver_compress_textures
 ("driver-compress-textures", false,
 ("driver-compress-textures", false,
  PRC_DESC("Set this true to ask the graphics driver to compress textures, "
  PRC_DESC("Set this true to ask the graphics driver to compress textures, "

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

@@ -36,7 +36,6 @@ extern EXPCL_PANDA_GOBJ ConfigVariableList exclude_texture_scale;
 
 
 
 
 extern EXPCL_PANDA_GOBJ ConfigVariableBool keep_texture_ram;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool keep_texture_ram;
-extern EXPCL_PANDA_GOBJ ConfigVariableBool compressed_textures;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool driver_compress_textures;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool driver_compress_textures;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool driver_generate_mipmaps;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool driver_generate_mipmaps;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool vertex_buffers;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool vertex_buffers;

+ 58 - 2
panda/src/gobj/lens.cxx

@@ -139,7 +139,7 @@ get_min_fov() const {
 
 
 /**
 /**
  * Returns the default near plane distance that will be assigned to each
  * Returns the default near plane distance that will be assigned to each
- * newly-created lens.  This is read from the Configrc file.
+ * newly-created lens.  This is read from the Config.prc file.
  */
  */
 PN_stdfloat Lens::
 PN_stdfloat Lens::
 get_default_near() {
 get_default_near() {
@@ -148,7 +148,7 @@ get_default_near() {
 
 
 /**
 /**
  * Returns the default far plane distance that will be assigned to each newly-
  * Returns the default far plane distance that will be assigned to each newly-
- * created lens.  This is read from the Configrc file.
+ * created lens.  This is read from the Config.prc file.
  */
  */
 PN_stdfloat Lens::
 PN_stdfloat Lens::
 get_default_far() {
 get_default_far() {
@@ -1930,6 +1930,35 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
   dg.add_stdfloat(_near_distance);
   dg.add_stdfloat(_near_distance);
   dg.add_stdfloat(_far_distance);
   dg.add_stdfloat(_far_distance);
   dg.add_uint16(_user_flags);
   dg.add_uint16(_user_flags);
+
+  if (manager->get_file_minor_ver() < 41) {
+    return;
+  }
+
+  dg.add_stdfloat(_min_fov);
+  dg.add_stdfloat(_interocular_distance);
+  dg.add_stdfloat(_convergence_distance);
+
+  if (_user_flags & UF_view_hpr) {
+    _view_hpr.write_datagram(dg);
+  }
+
+  if (_user_flags & UF_view_vector) {
+    _view_vector.write_datagram(dg);
+    _up_vector.write_datagram(dg);
+  }
+
+  if (_user_flags & UF_view_mat) {
+    _lens_mat.write_datagram(dg);
+  }
+
+  if (_user_flags & UF_keystone) {
+    _keystone.write_datagram(dg);
+  }
+
+  if (_user_flags & UF_custom_film_mat) {
+    _custom_film_mat.write_datagram(dg);
+  }
 }
 }
 
 
 /**
 /**
@@ -1949,6 +1978,33 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _far_distance = scan.get_stdfloat();
   _far_distance = scan.get_stdfloat();
   _user_flags = scan.get_uint16();
   _user_flags = scan.get_uint16();
 
 
+  if (manager->get_file_minor_ver() >= 41) {
+    _min_fov = scan.get_stdfloat();
+    _interocular_distance = scan.get_stdfloat();
+    _convergence_distance = scan.get_stdfloat();
+
+    if (_user_flags & UF_view_hpr) {
+      _view_hpr.read_datagram(scan);
+    }
+
+    if (_user_flags & UF_view_vector) {
+      _view_vector.read_datagram(scan);
+      _up_vector.read_datagram(scan);
+    }
+
+    if (_user_flags & UF_view_mat) {
+      _lens_mat.read_datagram(scan);
+    }
+
+    if (_user_flags & UF_keystone) {
+      _keystone.read_datagram(scan);
+    }
+
+    if (_user_flags & UF_custom_film_mat) {
+      _custom_film_mat.read_datagram(scan);
+    }
+  }
+
   _comp_flags = 0;
   _comp_flags = 0;
 }
 }
 
 

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

@@ -77,6 +77,23 @@ register_with_read_factory() {
   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
 }
 }
 
 
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void MatrixLens::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  dg.add_uint8(_ml_flags);
+  _user_mat.write_datagram(dg);
+
+  if (_ml_flags & MF_has_left_eye) {
+    _left_eye_mat.write_datagram(dg);
+  }
+  if (_ml_flags & MF_has_right_eye) {
+    _left_eye_mat.write_datagram(dg);
+  }
+}
+
 /**
 /**
  * This function is called by the BamReader's factory when a new object of
  * This function is called by the BamReader's factory when a new object of
  * type Lens is encountered in the Bam file.  It should create the Lens and
  * type Lens is encountered in the Bam file.  It should create the Lens and
@@ -93,3 +110,24 @@ make_from_bam(const FactoryParams &params) {
 
 
   return lens;
   return lens;
 }
 }
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new MatrixLens.
+ */
+void MatrixLens::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  Lens::fillin(scan, manager);
+
+  if (manager->get_file_minor_ver() >= 41) {
+    _ml_flags = scan.get_uint8();
+
+    _user_mat.read_datagram(scan);
+    if (_ml_flags & MF_has_left_eye) {
+      _left_eye_mat.read_datagram(scan);
+    }
+    if (_ml_flags & MF_has_right_eye) {
+      _right_eye_mat.read_datagram(scan);
+    }
+  }
+}

+ 2 - 0
panda/src/gobj/matrixLens.h

@@ -70,9 +70,11 @@ private:
 
 
 public:
 public:
   static void register_with_read_factory();
   static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
 
 
 protected:
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
   static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
 
 
 public:
 public:
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {

+ 9 - 6
panda/src/gobj/texture.I

@@ -1678,9 +1678,12 @@ clear_ram_mipmap_images() {
  */
  */
 INLINE void Texture::
 INLINE void Texture::
 generate_ram_mipmap_images() {
 generate_ram_mipmap_images() {
-  CDWriter cdata(_cycler, unlocked_ensure_ram_image(false));
+  // Don't use unlocked_ensure_ram_image here, because
+  // do_generate_ram_mipmap_images will want to decompress and recompress the
+  // image itself.
+  CDWriter cdata(_cycler, false);
   cdata->inc_image_modified();
   cdata->inc_image_modified();
-  do_generate_ram_mipmap_images(cdata);
+  do_generate_ram_mipmap_images(cdata, true);
 }
 }
 
 
 /**
 /**
@@ -2030,9 +2033,9 @@ set_component_type(Texture::ComponentType component_type) {
  * ram image needs to be replaced.
  * ram image needs to be replaced.
  */
  */
 INLINE void Texture::
 INLINE void Texture::
-set_loaded_from_image() {
+set_loaded_from_image(bool flag) {
   CDWriter cdata(_cycler, false);
   CDWriter cdata(_cycler, false);
-  cdata->_loaded_from_image = true;
+  cdata->_loaded_from_image = flag;
 }
 }
 
 
 /**
 /**
@@ -2051,9 +2054,9 @@ get_loaded_from_image() const {
  * when a Texture is loaded.
  * when a Texture is loaded.
  */
  */
 INLINE void Texture::
 INLINE void Texture::
-set_loaded_from_txo() {
+set_loaded_from_txo(bool flag) {
   CDWriter cdata(_cycler, false);
   CDWriter cdata(_cycler, false);
-  cdata->_loaded_from_txo = true;
+  cdata->_loaded_from_txo = flag;
 }
 }
 
 
 /**
 /**

+ 25 - 9
panda/src/gobj/texture.cxx

@@ -2708,7 +2708,8 @@ do_read(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpath,
       // If we intend to keep the ram image around, consider compressing it
       // If we intend to keep the ram image around, consider compressing it
       // etc.
       // etc.
       bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0);
       bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0);
-      do_consider_auto_process_ram_image(cdata, generate_mipmaps || uses_mipmaps(), true);
+      bool allow_compression = ((options.get_texture_flags() & LoaderOptions::TF_allow_compression) != 0);
+      do_consider_auto_process_ram_image(cdata, generate_mipmaps || uses_mipmaps(), allow_compression);
     }
     }
   }
   }
 
 
@@ -4277,7 +4278,12 @@ do_reload_ram_image(CData *cdata, bool allow_compression) {
   int orig_num_components = cdata->_num_components;
   int orig_num_components = cdata->_num_components;
 
 
   LoaderOptions options;
   LoaderOptions options;
-  options.set_texture_flags(LoaderOptions::TF_preload);
+  if (allow_compression) {
+    options.set_texture_flags(LoaderOptions::TF_preload |
+                              LoaderOptions::TF_allow_compression);
+  } else {
+    options.set_texture_flags(LoaderOptions::TF_preload);
+  }
   do_read(cdata, cdata->_fullpath, cdata->_alpha_fullpath,
   do_read(cdata, cdata->_fullpath, cdata->_alpha_fullpath,
           cdata->_primary_file_num_channels, cdata->_alpha_file_channel,
           cdata->_primary_file_num_channels, cdata->_alpha_file_channel,
           z, n, cdata->_has_read_pages, cdata->_has_read_mipmaps, options, NULL);
           z, n, cdata->_has_read_pages, cdata->_has_read_mipmaps, options, NULL);
@@ -4618,7 +4624,7 @@ do_consider_auto_process_ram_image(CData *cdata, bool generate_mipmaps,
 
 
   if (generate_mipmaps && !driver_generate_mipmaps &&
   if (generate_mipmaps && !driver_generate_mipmaps &&
       cdata->_ram_images.size() == 1) {
       cdata->_ram_images.size() == 1) {
-    do_generate_ram_mipmap_images(cdata);
+    do_generate_ram_mipmap_images(cdata, false);
     modified = true;
     modified = true;
   }
   }
 
 
@@ -4725,7 +4731,7 @@ do_compress_ram_image(CData *cdata, Texture::CompressionMode compression,
     if (!do_has_all_ram_mipmap_images(cdata)) {
     if (!do_has_all_ram_mipmap_images(cdata)) {
       // If we're about to compress the RAM image, we should ensure that we
       // If we're about to compress the RAM image, we should ensure that we
       // have all of the mipmap levels first.
       // have all of the mipmap levels first.
-      do_generate_ram_mipmap_images(cdata);
+      do_generate_ram_mipmap_images(cdata, false);
     }
     }
 
 
     RamImages compressed_ram_images;
     RamImages compressed_ram_images;
@@ -6232,7 +6238,10 @@ do_set_simple_ram_image(CData *cdata, CPTA_uchar image, int x_size, int y_size)
  */
  */
 int Texture::
 int Texture::
 do_get_expected_num_mipmap_levels(const CData *cdata) const {
 do_get_expected_num_mipmap_levels(const CData *cdata) const {
-  int size = max(cdata->_x_size, max(cdata->_y_size, cdata->_z_size));
+  int size = max(cdata->_x_size, cdata->_y_size);
+  if (cdata->_texture_type == Texture::TT_3d_texture) {
+    size = max(size, cdata->_z_size);
+  }
   int count = 1;
   int count = 1;
   while (size > 1) {
   while (size > 1) {
     size >>= 1;
     size >>= 1;
@@ -6331,10 +6340,12 @@ do_clear_ram_mipmap_images(CData *cdata) {
 }
 }
 
 
 /**
 /**
- *
+ * Generates the RAM mipmap images for this texture, first uncompressing it as
+ * required.  Will recompress the image if it was originally compressed,
+ * unless allow_recompress is true.
  */
  */
 void Texture::
 void Texture::
-do_generate_ram_mipmap_images(CData *cdata) {
+do_generate_ram_mipmap_images(CData *cdata, bool allow_recompress) {
   nassertv(do_has_ram_image(cdata));
   nassertv(do_has_ram_image(cdata));
 
 
   if (do_get_expected_num_mipmap_levels(cdata) == 1) {
   if (do_get_expected_num_mipmap_levels(cdata) == 1) {
@@ -6396,7 +6407,7 @@ do_generate_ram_mipmap_images(CData *cdata) {
     }
     }
   }
   }
 
 
-  if (orig_compression_mode != CM_off) {
+  if (orig_compression_mode != CM_off && allow_recompress) {
     // Now attempt to recompress the mipmap images according to the original
     // Now attempt to recompress the mipmap images according to the original
     // compression mode.  We don't need to bother compressing the first image
     // compression mode.  We don't need to bother compressing the first image
     // (it was already compressed, after all), so temporarily remove it from
     // (it was already compressed, after all), so temporarily remove it from
@@ -6415,6 +6426,11 @@ do_generate_ram_mipmap_images(CData *cdata) {
     bool success = do_compress_ram_image(cdata, orig_compression_mode, QL_default, NULL);
     bool success = do_compress_ram_image(cdata, orig_compression_mode, QL_default, NULL);
     // Now restore the toplevel image.
     // Now restore the toplevel image.
     if (success) {
     if (success) {
+      if (gobj_cat.is_debug()) {
+        gobj_cat.debug()
+          << "Compressed " << get_name() << " generated mipmaps with "
+          << cdata->_ram_image_compression << "\n";
+      }
       cdata->_ram_images.insert(cdata->_ram_images.begin(), orig_compressed_image);
       cdata->_ram_images.insert(cdata->_ram_images.begin(), orig_compressed_image);
     } else {
     } else {
       cdata->_ram_images.insert(cdata->_ram_images.begin(), uncompressed_image);
       cdata->_ram_images.insert(cdata->_ram_images.begin(), uncompressed_image);
@@ -8252,7 +8268,7 @@ do_squish(CData *cdata, Texture::CompressionMode compression, int squish_flags)
   if (!do_has_all_ram_mipmap_images(cdata)) {
   if (!do_has_all_ram_mipmap_images(cdata)) {
     // If we're about to compress the RAM image, we should ensure that we have
     // If we're about to compress the RAM image, we should ensure that we have
     // all of the mipmap levels first.
     // all of the mipmap levels first.
-    do_generate_ram_mipmap_images(cdata);
+    do_generate_ram_mipmap_images(cdata, false);
   }
   }
 
 
   RamImages compressed_ram_images;
   RamImages compressed_ram_images;

+ 103 - 48
panda/src/gobj/texture.h

@@ -300,70 +300,115 @@ PUBLISHED:
 
 
   INLINE bool has_filename() const;
   INLINE bool has_filename() const;
   INLINE const Filename &get_filename() const;
   INLINE const Filename &get_filename() const;
+  INLINE void set_filename(const Filename &filename);
+  INLINE void clear_filename();
+  MAKE_PROPERTY2(filename, has_filename, get_filename, set_filename, clear_filename);
+
   INLINE bool has_alpha_filename() const;
   INLINE bool has_alpha_filename() const;
   INLINE const Filename &get_alpha_filename() const;
   INLINE const Filename &get_alpha_filename() const;
-  MAKE_PROPERTY2(filename, has_filename, get_filename);
-  MAKE_PROPERTY2(alpha_filename, has_alpha_filename, get_alpha_filename);
+  INLINE void set_alpha_filename(const Filename &alpha_filename);
+  INLINE void clear_alpha_filename();
+  MAKE_PROPERTY2(alpha_filename, has_alpha_filename, get_alpha_filename, set_alpha_filename, clear_alpha_filename);
 
 
   INLINE bool has_fullpath() const;
   INLINE bool has_fullpath() const;
   INLINE const Filename &get_fullpath() const;
   INLINE const Filename &get_fullpath() const;
+  INLINE void set_fullpath(const Filename &fullpath);
+  INLINE void clear_fullpath();
+  MAKE_PROPERTY2(fullpath, has_fullpath, get_fullpath, set_fullpath, clear_fullpath);
+
   INLINE bool has_alpha_fullpath() const;
   INLINE bool has_alpha_fullpath() const;
   INLINE const Filename &get_alpha_fullpath() const;
   INLINE const Filename &get_alpha_fullpath() const;
-  MAKE_PROPERTY2(fullpath, has_fullpath, get_fullpath);
-  MAKE_PROPERTY2(alpha_fullpath, has_alpha_fullpath, get_alpha_fullpath);
+  INLINE void set_alpha_fullpath(const Filename &alpha_fullpath);
+  INLINE void clear_alpha_fullpath();
+  MAKE_PROPERTY2(alpha_fullpath, has_alpha_fullpath, get_alpha_fullpath, set_alpha_fullpath, clear_alpha_fullpath);
 
 
   INLINE int get_x_size() const;
   INLINE int get_x_size() const;
+  INLINE void set_x_size(int x_size);
+  MAKE_PROPERTY(x_size, get_x_size, set_x_size);
+
   INLINE int get_y_size() const;
   INLINE int get_y_size() const;
+  INLINE void set_y_size(int y_size);
+  MAKE_PROPERTY(y_size, get_y_size, set_y_size);
+
   INLINE int get_z_size() const;
   INLINE int get_z_size() const;
+  INLINE void set_z_size(int z_size);
+  MAKE_PROPERTY(z_size, get_z_size, set_z_size);
+
   INLINE int get_num_views() const;
   INLINE int get_num_views() const;
+  INLINE void set_num_views(int num_views);
+  MAKE_PROPERTY(num_views, get_num_views, set_num_views);
+
   INLINE int get_num_pages() const;
   INLINE int get_num_pages() const;
   INLINE int get_num_components() const;
   INLINE int get_num_components() const;
   INLINE int get_component_width() const;
   INLINE int get_component_width() const;
   INLINE TextureType get_texture_type() const;
   INLINE TextureType get_texture_type() const;
-  INLINE Format get_format() const;
-  INLINE ComponentType get_component_type() const;
   INLINE GeomEnums::UsageHint get_usage_hint() const;
   INLINE GeomEnums::UsageHint get_usage_hint() const;
-  MAKE_PROPERTY(num_views, get_num_views);
+
   MAKE_PROPERTY(num_pages, get_num_pages);
   MAKE_PROPERTY(num_pages, get_num_pages);
   MAKE_PROPERTY(num_components, get_num_components);
   MAKE_PROPERTY(num_components, get_num_components);
   MAKE_PROPERTY(component_width, get_component_width);
   MAKE_PROPERTY(component_width, get_component_width);
   MAKE_PROPERTY(texture_type, get_texture_type);
   MAKE_PROPERTY(texture_type, get_texture_type);
-  MAKE_PROPERTY(format, get_format);
-  MAKE_PROPERTY(component_type, get_component_type);
   MAKE_PROPERTY(usage_hint, get_usage_hint);
   MAKE_PROPERTY(usage_hint, get_usage_hint);
 
 
-  INLINE void set_wrap_u(WrapMode wrap);
-  INLINE void set_wrap_v(WrapMode wrap);
-  INLINE void set_wrap_w(WrapMode wrap);
-  INLINE void set_minfilter(FilterType filter);
-  INLINE void set_magfilter(FilterType filter);
-  INLINE void set_anisotropic_degree(int anisotropic_degree);
-  INLINE void set_border_color(const LColor &color);
-  INLINE void set_compression(CompressionMode compression);
-  INLINE void set_render_to_texture(bool render_to_texture);
+  INLINE Format get_format() const;
+  INLINE void set_format(Format format);
+  MAKE_PROPERTY(format, get_format, set_format);
+
+  INLINE ComponentType get_component_type() const;
+  INLINE void set_component_type(ComponentType component_type);
+  MAKE_PROPERTY(component_type, get_component_type, set_component_type);
 
 
-  INLINE const SamplerState &get_default_sampler() const;
-  INLINE void set_default_sampler(const SamplerState &sampler);
   INLINE SamplerState::WrapMode get_wrap_u() const;
   INLINE SamplerState::WrapMode get_wrap_u() const;
+  INLINE void set_wrap_u(WrapMode wrap);
+  MAKE_PROPERTY(wrap_u, get_wrap_u, set_wrap_u);
+
   INLINE SamplerState::WrapMode get_wrap_v() const;
   INLINE SamplerState::WrapMode get_wrap_v() const;
+  INLINE void set_wrap_v(WrapMode wrap);
+  MAKE_PROPERTY(wrap_v, get_wrap_v, set_wrap_v);
+
   INLINE SamplerState::WrapMode get_wrap_w() const;
   INLINE SamplerState::WrapMode get_wrap_w() const;
+  INLINE void set_wrap_w(WrapMode wrap);
+  MAKE_PROPERTY(wrap_w, get_wrap_w, set_wrap_w);
+
   INLINE SamplerState::FilterType get_minfilter() const;
   INLINE SamplerState::FilterType get_minfilter() const;
-  INLINE SamplerState::FilterType get_magfilter() const;
   INLINE SamplerState::FilterType get_effective_minfilter() const;
   INLINE SamplerState::FilterType get_effective_minfilter() const;
+  INLINE void set_minfilter(FilterType filter);
+  MAKE_PROPERTY(minfilter, get_minfilter, set_minfilter);
+  MAKE_PROPERTY(effective_minfilter, get_effective_minfilter);
+
+  INLINE SamplerState::FilterType get_magfilter() const;
   INLINE SamplerState::FilterType get_effective_magfilter() const;
   INLINE SamplerState::FilterType get_effective_magfilter() const;
+  INLINE void set_magfilter(FilterType filter);
+  MAKE_PROPERTY(magfilter, get_magfilter, set_magfilter);
+  MAKE_PROPERTY(effective_magfilter, get_effective_magfilter);
+
   INLINE int get_anisotropic_degree() const;
   INLINE int get_anisotropic_degree() const;
   INLINE int get_effective_anisotropic_degree() const;
   INLINE int get_effective_anisotropic_degree() const;
+  INLINE void set_anisotropic_degree(int anisotropic_degree);
+  MAKE_PROPERTY(anisotropic_degree, get_anisotropic_degree, set_anisotropic_degree);
+  MAKE_PROPERTY(effective_anisotropic_degree, get_effective_anisotropic_degree);
+
   INLINE LColor get_border_color() const;
   INLINE LColor get_border_color() const;
-  INLINE CompressionMode get_compression() const;
+  INLINE void set_border_color(const LColor &color);
+  MAKE_PROPERTY(border_color, get_border_color, set_border_color);
+
   INLINE bool has_compression() const;
   INLINE bool has_compression() const;
+  INLINE CompressionMode get_compression() const;
+  INLINE void set_compression(CompressionMode compression);
+  MAKE_PROPERTY(compression, get_compression, set_compression); // Could maybe use has_compression here, too
+
   INLINE bool get_render_to_texture() const;
   INLINE bool get_render_to_texture() const;
-  INLINE bool uses_mipmaps() const;
+  INLINE void set_render_to_texture(bool render_to_texture);
+  MAKE_PROPERTY(render_to_texture, get_render_to_texture, set_render_to_texture);
+
+  INLINE const SamplerState &get_default_sampler() const;
+  INLINE void set_default_sampler(const SamplerState &sampler);
   MAKE_PROPERTY(default_sampler, get_default_sampler, set_default_sampler);
   MAKE_PROPERTY(default_sampler, get_default_sampler, set_default_sampler);
-  MAKE_PROPERTY(compression, get_compression, set_compression);
+  INLINE bool uses_mipmaps() const;
 
 
-  INLINE void set_quality_level(QualityLevel quality_level);
   INLINE QualityLevel get_quality_level() const;
   INLINE QualityLevel get_quality_level() const;
   INLINE QualityLevel get_effective_quality_level() const;
   INLINE QualityLevel get_effective_quality_level() const;
+  INLINE void set_quality_level(QualityLevel quality_level);
   MAKE_PROPERTY(quality_level, get_quality_level, set_quality_level);
   MAKE_PROPERTY(quality_level, get_quality_level, set_quality_level);
   MAKE_PROPERTY(effective_quality_level, get_effective_quality_level);
   MAKE_PROPERTY(effective_quality_level, get_effective_quality_level);
 
 
@@ -372,6 +417,7 @@ PUBLISHED:
   INLINE int get_expected_mipmap_y_size(int n) const;
   INLINE int get_expected_mipmap_y_size(int n) const;
   INLINE int get_expected_mipmap_z_size(int n) const;
   INLINE int get_expected_mipmap_z_size(int n) const;
   INLINE int get_expected_mipmap_num_pages(int n) const;
   INLINE int get_expected_mipmap_num_pages(int n) const;
+  MAKE_PROPERTY(expected_num_mipmap_levels, get_expected_num_mipmap_levels);
 
 
   INLINE bool has_ram_image() const;
   INLINE bool has_ram_image() const;
   INLINE bool has_uncompressed_ram_image() const;
   INLINE bool has_uncompressed_ram_image() const;
@@ -381,6 +427,12 @@ PUBLISHED:
   INLINE size_t get_ram_page_size() const;
   INLINE size_t get_ram_page_size() const;
   INLINE size_t get_expected_ram_image_size() const;
   INLINE size_t get_expected_ram_image_size() const;
   INLINE size_t get_expected_ram_page_size() const;
   INLINE size_t get_expected_ram_page_size() const;
+  MAKE_PROPERTY(ram_image_size, get_ram_image_size);
+  MAKE_PROPERTY(ram_view_size, get_ram_view_size);
+  MAKE_PROPERTY(ram_page_size, get_ram_page_size);
+  MAKE_PROPERTY(expected_ram_image_size, get_expected_ram_image_size);
+  MAKE_PROPERTY(expected_ram_page_size, get_expected_ram_page_size);
+
   INLINE CPTA_uchar get_ram_image();
   INLINE CPTA_uchar get_ram_image();
   INLINE CompressionMode get_ram_image_compression() const;
   INLINE CompressionMode get_ram_image_compression() const;
   INLINE CPTA_uchar get_uncompressed_ram_image();
   INLINE CPTA_uchar get_uncompressed_ram_image();
@@ -395,6 +447,10 @@ PUBLISHED:
   virtual bool get_keep_ram_image() const;
   virtual bool get_keep_ram_image() const;
   virtual bool is_cacheable() const;
   virtual bool is_cacheable() const;
 
 
+  MAKE_PROPERTY(ram_image_compression, get_ram_image_compression);
+  MAKE_PROPERTY(keep_ram_image, get_keep_ram_image, set_keep_ram_image);
+  MAKE_PROPERTY(cacheable, is_cacheable);
+
   INLINE bool compress_ram_image(CompressionMode compression = CM_on,
   INLINE bool compress_ram_image(CompressionMode compression = CM_on,
                                  QualityLevel quality_level = QL_default,
                                  QualityLevel quality_level = QL_default,
                                  GraphicsStateGuardianBase *gsg = NULL);
                                  GraphicsStateGuardianBase *gsg = NULL);
@@ -421,6 +477,9 @@ PUBLISHED:
   INLINE void clear_ram_mipmap_images();
   INLINE void clear_ram_mipmap_images();
   INLINE void generate_ram_mipmap_images();
   INLINE void generate_ram_mipmap_images();
 
 
+  MAKE_PROPERTY(num_ram_mipmap_images, get_num_ram_mipmap_images);
+  MAKE_PROPERTY(num_loadable_ram_mipmap_images, get_num_loadable_ram_mipmap_images);
+
   INLINE int get_simple_x_size() const;
   INLINE int get_simple_x_size() const;
   INLINE int get_simple_y_size() const;
   INLINE int get_simple_y_size() const;
   INLINE bool has_simple_ram_image() const;
   INLINE bool has_simple_ram_image() const;
@@ -432,6 +491,10 @@ PUBLISHED:
   void generate_simple_ram_image();
   void generate_simple_ram_image();
   INLINE void clear_simple_ram_image();
   INLINE void clear_simple_ram_image();
 
 
+  MAKE_PROPERTY(simple_x_size, get_simple_x_size);
+  MAKE_PROPERTY(simple_y_size, get_simple_y_size);
+  MAKE_PROPERTY2(simple_ram_image, has_simple_ram_image, get_simple_ram_image);
+
   PT(TexturePeeker) peek();
   PT(TexturePeeker) peek();
 
 
   INLINE UpdateSeq get_properties_modified() const;
   INLINE UpdateSeq get_properties_modified() const;
@@ -441,9 +504,9 @@ PUBLISHED:
   MAKE_PROPERTY(image_modified, get_image_modified);
   MAKE_PROPERTY(image_modified, get_image_modified);
   MAKE_PROPERTY(simple_image_modified, get_simple_image_modified);
   MAKE_PROPERTY(simple_image_modified, get_simple_image_modified);
 
 
-  INLINE void set_auto_texture_scale(AutoTextureScale scale);
-  INLINE AutoTextureScale get_auto_texture_scale() const;
   INLINE bool has_auto_texture_scale() const;
   INLINE bool has_auto_texture_scale() const;
+  INLINE AutoTextureScale get_auto_texture_scale() const;
+  INLINE void set_auto_texture_scale(AutoTextureScale scale);
   MAKE_PROPERTY(auto_texture_scale, get_auto_texture_scale,
   MAKE_PROPERTY(auto_texture_scale, get_auto_texture_scale,
                                     set_auto_texture_scale);
                                     set_auto_texture_scale);
 
 
@@ -470,22 +533,6 @@ PUBLISHED:
   INLINE static bool has_textures_power_2();
   INLINE static bool has_textures_power_2();
 
 
 PUBLISHED:
 PUBLISHED:
-  // These are published, but in general, you shouldn't be mucking with these
-  // values; they are set automatically when a texture is loaded.
-  INLINE void set_filename(const Filename &filename);
-  INLINE void clear_filename();
-  INLINE void set_alpha_filename(const Filename &alpha_filename);
-  INLINE void clear_alpha_filename();
-
-  INLINE void set_fullpath(const Filename &fullpath);
-  INLINE void clear_fullpath();
-  INLINE void set_alpha_fullpath(const Filename &alpha_fullpath);
-  INLINE void clear_alpha_fullpath();
-
-  INLINE void set_x_size(int x_size);
-  INLINE void set_y_size(int y_size);
-  INLINE void set_z_size(int z_size);
-  INLINE void set_num_views(int num_views);
 
 
   INLINE int get_pad_x_size() const;
   INLINE int get_pad_x_size() const;
   INLINE int get_pad_y_size() const;
   INLINE int get_pad_y_size() const;
@@ -499,21 +546,29 @@ PUBLISHED:
   INLINE int get_orig_file_y_size() const;
   INLINE int get_orig_file_y_size() const;
   INLINE int get_orig_file_z_size() const;
   INLINE int get_orig_file_z_size() const;
 
 
+  MAKE_PROPERTY(orig_file_x_size, get_orig_file_x_size);
+  MAKE_PROPERTY(orig_file_y_size, get_orig_file_y_size);
+  MAKE_PROPERTY(orig_file_z_size, get_orig_file_z_size);
+
   void set_orig_file_size(int x, int y, int z = 1);
   void set_orig_file_size(int x, int y, int z = 1);
 
 
-  INLINE void set_format(Format format);
-  INLINE void set_component_type(ComponentType component_type);
-  INLINE void set_loaded_from_image();
+  INLINE void set_loaded_from_image(bool flag = true);
   INLINE bool get_loaded_from_image() const;
   INLINE bool get_loaded_from_image() const;
+  MAKE_PROPERTY(loaded_from_image, get_loaded_from_image, set_loaded_from_image);
 
 
-  INLINE void set_loaded_from_txo();
+  INLINE void set_loaded_from_txo(bool flag = true);
   INLINE bool get_loaded_from_txo() const;
   INLINE bool get_loaded_from_txo() const;
+  MAKE_PROPERTY(loaded_from_txo, get_loaded_from_txo, set_loaded_from_txo);
 
 
   INLINE bool get_match_framebuffer_format() const;
   INLINE bool get_match_framebuffer_format() const;
   INLINE void set_match_framebuffer_format(bool flag);
   INLINE void set_match_framebuffer_format(bool flag);
+  MAKE_PROPERTY(match_framebuffer_format, get_match_framebuffer_format,
+                                          set_match_framebuffer_format);
 
 
   INLINE bool get_post_load_store_cache() const;
   INLINE bool get_post_load_store_cache() const;
   INLINE void set_post_load_store_cache(bool flag);
   INLINE void set_post_load_store_cache(bool flag);
+  MAKE_PROPERTY(post_load_store_cache, get_post_load_store_cache,
+                                       set_post_load_store_cache);
 
 
   TextureContext *prepare_now(int view,
   TextureContext *prepare_now(int view,
                               PreparedGraphicsObjects *prepared_objects,
                               PreparedGraphicsObjects *prepared_objects,
@@ -697,7 +752,7 @@ protected:
   INLINE void do_clear_ram_image(CData *cdata);
   INLINE void do_clear_ram_image(CData *cdata);
   void do_clear_simple_ram_image(CData *cdata);
   void do_clear_simple_ram_image(CData *cdata);
   void do_clear_ram_mipmap_images(CData *cdata);
   void do_clear_ram_mipmap_images(CData *cdata);
-  void do_generate_ram_mipmap_images(CData *cdata);
+  void do_generate_ram_mipmap_images(CData *cdata, bool allow_recompress);
   void do_set_pad_size(CData *cdata, int x, int y, int z);
   void do_set_pad_size(CData *cdata, int x, int y, int z);
   virtual bool do_can_reload(const CData *cdata) const;
   virtual bool do_can_reload(const CData *cdata) const;
   bool do_reload(CData *cdata);
   bool do_reload(CData *cdata);

+ 8 - 0
panda/src/gobj/texturePeeker.I

@@ -48,3 +48,11 @@ INLINE int TexturePeeker::
 get_z_size() const {
 get_z_size() const {
   return _z_size;
   return _z_size;
 }
 }
+
+/**
+ * Returns whether a given coordinate is inside of the texture dimensions.
+ */
+INLINE bool TexturePeeker::
+has_pixel(size_t x, size_t y) const {
+  return x < _x_size && y < _y_size;
+}

+ 72 - 2
panda/src/gobj/texturePeeker.cxx

@@ -91,8 +91,11 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) {
   switch (_format) {
   switch (_format) {
   case Texture::F_depth_stencil:
   case Texture::F_depth_stencil:
   case Texture::F_depth_component:
   case Texture::F_depth_component:
-
+  case Texture::F_depth_component16:
+  case Texture::F_depth_component24:
+  case Texture::F_depth_component32:
   case Texture::F_red:
   case Texture::F_red:
+  case Texture::F_r16:
     _get_texel = get_texel_r;
     _get_texel = get_texel_r;
     break;
     break;
 
 
@@ -109,23 +112,30 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) {
     break;
     break;
 
 
   case Texture::F_luminance:
   case Texture::F_luminance:
+  case Texture::F_sluminance:
     _get_texel = get_texel_l;
     _get_texel = get_texel_l;
     break;
     break;
 
 
   case Texture::F_luminance_alpha:
   case Texture::F_luminance_alpha:
+  case Texture::F_sluminance_alpha:
   case Texture::F_luminance_alphamask:
   case Texture::F_luminance_alphamask:
     _get_texel = get_texel_la;
     _get_texel = get_texel_la;
     break;
     break;
 
 
   case Texture::F_rgb:
   case Texture::F_rgb:
+  case Texture::F_srgb:
   case Texture::F_rgb5:
   case Texture::F_rgb5:
   case Texture::F_rgb8:
   case Texture::F_rgb8:
   case Texture::F_rgb12:
   case Texture::F_rgb12:
+  case Texture::F_rgb16:
   case Texture::F_rgb332:
   case Texture::F_rgb332:
+  case Texture::F_r11_g11_b10:
+  case Texture::F_rgb9_e5:
     _get_texel = get_texel_rgb;
     _get_texel = get_texel_rgb;
     break;
     break;
 
 
   case Texture::F_rgba:
   case Texture::F_rgba:
+  case Texture::F_srgb_alpha:
   case Texture::F_rgbm:
   case Texture::F_rgbm:
   case Texture::F_rgba4:
   case Texture::F_rgba4:
   case Texture::F_rgba5:
   case Texture::F_rgba5:
@@ -133,10 +143,13 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) {
   case Texture::F_rgba12:
   case Texture::F_rgba12:
   case Texture::F_rgba16:
   case Texture::F_rgba16:
   case Texture::F_rgba32:
   case Texture::F_rgba32:
+  case Texture::F_rgb10_a2:
     _get_texel = get_texel_rgba;
     _get_texel = get_texel_rgba;
     break;
     break;
   default:
   default:
     // Not supported.
     // Not supported.
+    gobj_cat.error() << "Unsupported texture peeker format: "
+      << Texture::format_format(_format) << endl;
     _image.clear();
     _image.clear();
     return;
     return;
   }
   }
@@ -155,13 +168,70 @@ void TexturePeeker::
 lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const {
 lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const {
   int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size;
   int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size;
   int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size;
   int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size;
+  fetch_pixel(color, x, y);
+}
 
 
+/**
+ *  Works like TexturePeeker::lookup(), but instead uv-coordinates integer
+ *  coordinates are used.
+ */
+void TexturePeeker::
+fetch_pixel(LColor& color, size_t x, size_t y) const {
   nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size);
   nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size);
   const unsigned char *p = _image.p() + (y * _x_size + x) * _pixel_width;
   const unsigned char *p = _image.p() + (y * _x_size + x) * _pixel_width;
-
   (*_get_texel)(color, p, _get_component);
   (*_get_texel)(color, p, _get_component);
 }
 }
 
 
+
+/**
+ * Performs a bilinear lookup to retrieve the color value stored at the uv
+ * coordinate (u, v).
+ *
+ * In case the point is outside of the uv range, color is set to zero,
+ * and false is returned.  Otherwise true is returned.
+ */
+bool TexturePeeker::
+lookup_bilinear(LColor &color, PN_stdfloat u, PN_stdfloat v) const {
+  color = LColor::zero();
+
+  u = u * _x_size - 0.5;
+  v = v * _y_size - 0.5;
+
+  int min_u = int(floor(u));
+  int min_v = int(floor(v));
+
+  PN_stdfloat frac_u = u - min_u;
+  PN_stdfloat frac_v = v - min_v;
+
+  LColor p00(LColor::zero()), p01(LColor::zero()), p10(LColor::zero()), p11(LColor::zero());
+  PN_stdfloat w00 = 0.0, w01 = 0.0, w10 = 0.0, w11 = 0.0;
+
+  if (has_pixel(min_u, min_v)) {
+    w00 = (1.0 - frac_v) * (1.0 - frac_u);
+    fetch_pixel(p00, min_u, min_v);
+  }
+  if (has_pixel(min_u + 1, min_v)) {
+    w10 = (1.0 - frac_v) * frac_u;
+    fetch_pixel(p10, min_u + 1, min_v);
+  }
+  if (has_pixel(min_u, min_v + 1)) {
+    w01 = frac_v * (1.0 - frac_u);
+    fetch_pixel(p01, min_u, min_v + 1);
+  }
+  if (has_pixel(min_u + 1, min_v + 1)) {
+    w11 = frac_v * frac_u;
+    fetch_pixel(p11, min_u + 1, min_v + 1);
+  }
+
+  PN_stdfloat net_w = w00 + w01 + w10 + w11;
+  if (net_w == 0.0) {
+    return false;
+  }
+
+  color = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11) / net_w;
+  return true;
+}
+
 /**
 /**
  * Fills "color" with the RGBA color of the texel at point (u, v, w).
  * Fills "color" with the RGBA color of the texel at point (u, v, w).
  *
  *

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

@@ -36,8 +36,11 @@ PUBLISHED:
   INLINE int get_y_size() const;
   INLINE int get_y_size() const;
   INLINE int get_z_size() const;
   INLINE int get_z_size() const;
 
 
+  INLINE bool has_pixel(size_t x, size_t y) const;
   void lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const;
   void lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const;
   void lookup(LColor &color, PN_stdfloat u, PN_stdfloat v, PN_stdfloat w) const;
   void lookup(LColor &color, PN_stdfloat u, PN_stdfloat v, PN_stdfloat w) const;
+  void fetch_pixel(LColor &color, size_t x, size_t y) const;
+  bool lookup_bilinear(LColor &color, PN_stdfloat u, PN_stdfloat v) const;
   void filter_rect(LColor &color,
   void filter_rect(LColor &color,
                    PN_stdfloat min_u, PN_stdfloat min_v,
                    PN_stdfloat min_u, PN_stdfloat min_v,
                    PN_stdfloat max_u, PN_stdfloat max_v) const;
                    PN_stdfloat max_u, PN_stdfloat max_v) const;

+ 1 - 0
panda/src/particlesystem/colorInterpolationManager.cxx

@@ -11,6 +11,7 @@
  * @date 2005-06-02
  * @date 2005-06-02
  */
  */
 
 
+#include "colorInterpolationManager.h"
 #include "mathNumbers.h"
 #include "mathNumbers.h"
 
 
 TypeHandle ColorInterpolationFunction::_type_handle;
 TypeHandle ColorInterpolationFunction::_type_handle;

+ 24 - 2
panda/src/pgraph/camera.cxx

@@ -26,9 +26,9 @@ Camera(const string &name, Lens *lens) :
   LensNode(name, lens),
   LensNode(name, lens),
   _active(true),
   _active(true),
   _camera_mask(~PandaNode::get_overall_bit()),
   _camera_mask(~PandaNode::get_overall_bit()),
-  _initial_state(RenderState::make_empty())
+  _initial_state(RenderState::make_empty()),
+  _lod_scale(1)
 {
 {
-  set_lod_scale(1.0);
 }
 }
 
 
 /**
 /**
@@ -271,6 +271,23 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 
 
   dg.add_bool(_active);
   dg.add_bool(_active);
   dg.add_uint32(_camera_mask.get_word());
   dg.add_uint32(_camera_mask.get_word());
+
+  manager->write_pointer(dg, _initial_state);
+  dg.add_stdfloat(_lod_scale);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Camera::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int Camera::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = LensNode::complete_pointers(p_list, manager);
+  _initial_state = DCAST(RenderState, p_list[pi++]);
+  return pi;
 }
 }
 
 
 /**
 /**
@@ -300,4 +317,9 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 
 
   _active = scan.get_bool();
   _active = scan.get_bool();
   _camera_mask.set_word(scan.get_uint32());
   _camera_mask.set_word(scan.get_uint32());
+
+  if (manager->get_file_minor_ver() >= 41) {
+    manager->read_pointer(scan);
+    _lod_scale = scan.get_stdfloat();
+  }
 }
 }

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

@@ -124,6 +124,8 @@ private:
 public:
 public:
   static void register_with_read_factory();
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist,
+                                BamReader *manager);
 
 
 protected:
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
   static TypedWritable *make_from_bam(const FactoryParams &params);

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

@@ -131,6 +131,7 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
   if (object->_state->get_attrib(trans)) {
   if (object->_state->get_attrib(trans)) {
     switch (trans->get_mode()) {
     switch (trans->get_mode()) {
     case TransparencyAttrib::M_alpha:
     case TransparencyAttrib::M_alpha:
+    case TransparencyAttrib::M_premultiplied_alpha:
       // M_alpha implies an alpha-write test, so we don't waste time writing
       // M_alpha implies an alpha-write test, so we don't waste time writing
       // 0-valued pixels.
       // 0-valued pixels.
       object->_state = object->_state->compose(get_alpha_state());
       object->_state = object->_state->compose(get_alpha_state());

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

@@ -47,7 +47,7 @@ apply_transform_and_state(CullTraverser *trav) {
   _node_reader.compose_draw_mask(_draw_mask);
   _node_reader.compose_draw_mask(_draw_mask);
 
 
   apply_transform_and_state(trav, _node_reader.get_transform(),
   apply_transform_and_state(trav, _node_reader.get_transform(),
-                            node_state, _node_reader.get_effects(),
+                            MOVE(node_state), _node_reader.get_effects(),
                             _node_reader.get_off_clip_planes());
                             _node_reader.get_off_clip_planes());
 }
 }
 
 

+ 35 - 5
panda/src/pgraph/lensNode.cxx

@@ -218,9 +218,18 @@ void LensNode::
 write_datagram(BamWriter *manager, Datagram &dg) {
 write_datagram(BamWriter *manager, Datagram &dg) {
   PandaNode::write_datagram(manager, dg);
   PandaNode::write_datagram(manager, dg);
 
 
-  // For now, we only write out lens 0, simply because that's what we always
-  // have done.  Should probably write out all lenses for the future.
-  manager->write_pointer(dg, get_lens(0));
+  if (manager->get_file_minor_ver() < 41) {
+    // Prior to bam 6.41, we stored only one lens.
+    manager->write_pointer(dg, get_lens(0));
+  } else {
+    dg.add_uint16(_lenses.size());
+
+    Lenses::const_iterator li;
+    for (li = _lenses.begin(); li != _lenses.end(); ++li) {
+      manager->write_pointer(dg, (*li)._lens);
+      dg.add_bool((*li)._is_active);
+    }
+  }
 }
 }
 
 
 /**
 /**
@@ -230,7 +239,16 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 int LensNode::
 int LensNode::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = PandaNode::complete_pointers(p_list, manager);
   int pi = PandaNode::complete_pointers(p_list, manager);
-  set_lens(0, DCAST(Lens, p_list[pi++]));
+
+  Lenses::iterator li;
+  for (li = _lenses.begin(); li != _lenses.end(); ++li) {
+    (*li)._lens = DCAST(Lens, p_list[pi++]);
+  }
+
+  if (_shown_frustum != (PandaNode *)NULL) {
+    show_frustum();
+  }
+
   return pi;
   return pi;
 }
 }
 
 
@@ -259,5 +277,17 @@ void LensNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   PandaNode::fillin(scan, manager);
   PandaNode::fillin(scan, manager);
 
 
-  manager->read_pointer(scan);
+  if (manager->get_file_minor_ver() < 41) {
+    // Prior to bam 6.41, we stored only one lens.
+    _lenses.resize(1);
+    manager->read_pointer(scan);
+
+  } else {
+    _lenses.resize(scan.get_uint16());
+    Lenses::iterator li;
+    for (li = _lenses.begin(); li != _lenses.end(); ++li) {
+      manager->read_pointer(scan);
+      (*li)._is_active = scan.get_bool();
+    }
+  }
 }
 }

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

@@ -3750,6 +3750,8 @@ void PandaNode::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
   TypedWritable::fillin(scan, manager);
 
 
+  remove_all_children();
+
   string name = scan.get_string();
   string name = scan.get_string();
   set_name(name);
   set_name(name);
 
 

+ 5 - 4
panda/src/pgraph/renderState.cxx

@@ -1853,8 +1853,8 @@ determine_bin_index() {
   string bin_name;
   string bin_name;
   _draw_order = 0;
   _draw_order = 0;
 
 
-  const CullBinAttrib *bin = DCAST(CullBinAttrib, get_attrib(CullBinAttrib::get_class_slot()));
-  if (bin != (const CullBinAttrib *)NULL) {
+  const CullBinAttrib *bin;
+  if (get_attrib(bin)) {
     bin_name = bin->get_bin_name();
     bin_name = bin->get_bin_name();
     _draw_order = bin->get_draw_order();
     _draw_order = bin->get_draw_order();
   }
   }
@@ -1864,10 +1864,11 @@ determine_bin_index() {
     // opaque or transparent, based on the transparency setting.
     // opaque or transparent, based on the transparency setting.
     bin_name = "opaque";
     bin_name = "opaque";
 
 
-    const TransparencyAttrib *transparency = DCAST(TransparencyAttrib, get_attrib(TransparencyAttrib::get_class_slot()));
-    if (transparency != (const TransparencyAttrib *)NULL) {
+    const TransparencyAttrib *transparency;
+    if (get_attrib(transparency)) {
       switch (transparency->get_mode()) {
       switch (transparency->get_mode()) {
       case TransparencyAttrib::M_alpha:
       case TransparencyAttrib::M_alpha:
+      case TransparencyAttrib::M_premultiplied_alpha:
       case TransparencyAttrib::M_dual:
       case TransparencyAttrib::M_dual:
         // These transparency modes require special back-to-front sorting.
         // These transparency modes require special back-to-front sorting.
         bin_name = "transparent";
         bin_name = "transparent";

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

@@ -55,6 +55,10 @@ output(ostream &out) const {
     out << "alpha";
     out << "alpha";
     break;
     break;
 
 
+  case M_premultiplied_alpha:
+    out << "premultiplied alpha";
+    break;
+
   case M_multisample:
   case M_multisample:
     out << "multisample";
     out << "multisample";
     break;
     break;
@@ -70,9 +74,6 @@ output(ostream &out) const {
   case M_dual:
   case M_dual:
     out << "dual";
     out << "dual";
     break;
     break;
-
-  case M_notused:
-    break;
   }
   }
 }
 }
 
 

+ 1 - 1
panda/src/pgraph/transparencyAttrib.h

@@ -36,7 +36,7 @@ PUBLISHED:
     // corresponded to M_none or M_alpha).
     // corresponded to M_none or M_alpha).
     M_none = 0,         // No transparency.
     M_none = 0,         // No transparency.
     M_alpha = 1,        // Normal transparency, panda will sort back-to-front.
     M_alpha = 1,        // Normal transparency, panda will sort back-to-front.
-    M_notused,          // Unused placeholder.  Do not use this.
+    M_premultiplied_alpha, // Assume textures use premultiplied alpha.
     M_multisample,      // Uses ms buffer, alpha values modified to 1.0.
     M_multisample,      // Uses ms buffer, alpha values modified to 1.0.
     M_multisample_mask, // Uses ms buffer, alpha values not modified.
     M_multisample_mask, // Uses ms buffer, alpha values not modified.
     M_binary,           // Only writes pixels with alpha >= 0.5.
     M_binary,           // Only writes pixels with alpha >= 0.5.

+ 21 - 3
panda/src/pgraphnodes/lightLensNode.I

@@ -42,12 +42,11 @@ set_shadow_caster(bool caster) {
  */
  */
 INLINE void LightLensNode::
 INLINE void LightLensNode::
 set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_sort) {
 set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_sort) {
-  if ((_shadow_caster && !caster) || buffer_xsize != _sb_xsize || buffer_ysize != _sb_ysize) {
+  if ((_shadow_caster && !caster) || buffer_xsize != _sb_size[0] || buffer_ysize != _sb_size[1]) {
     clear_shadow_buffers();
     clear_shadow_buffers();
   }
   }
   _shadow_caster = caster;
   _shadow_caster = caster;
-  _sb_xsize = buffer_xsize;
-  _sb_ysize = buffer_ysize;
+  _sb_size.set(buffer_xsize, buffer_ysize);
 
 
   if (buffer_sort != _sb_sort) {
   if (buffer_sort != _sb_sort) {
     ShadowBuffers::iterator it;
     ShadowBuffers::iterator it;
@@ -59,6 +58,25 @@ set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_so
   set_active(caster);
   set_active(caster);
 }
 }
 
 
+/**
+ * Returns the size of the shadow buffer to be created for this light source.
+ */
+INLINE LVecBase2i LightLensNode::
+get_shadow_buffer_size() const {
+  return _sb_size;
+}
+
+/**
+ * Sets the size of the shadow buffer to be created for this light source.
+ */
+INLINE void LightLensNode::
+set_shadow_buffer_size(const LVecBase2i &size) {
+  if (size != _sb_size) {
+    clear_shadow_buffers();
+  }
+  _sb_size = size;
+}
+
 /**
 /**
  * Returns the buffer that has been constructed for a given GSG, or NULL if no
  * Returns the buffer that has been constructed for a given GSG, or NULL if no
  * such buffer has (yet) been constructed.  This should be used for debugging
  * such buffer has (yet) been constructed.  This should be used for debugging

+ 4 - 6
panda/src/pgraphnodes/lightLensNode.cxx

@@ -31,8 +31,7 @@ LightLensNode(const string &name, Lens *lens) :
 {
 {
   set_active(false);
   set_active(false);
   _shadow_caster = false;
   _shadow_caster = false;
-  _sb_xsize = 512;
-  _sb_ysize = 512;
+  _sb_size.set(512, 512);
   _sb_sort = -10;
   _sb_sort = -10;
   // set_initial_state(RenderState::make(ShaderAttrib::make_off(), 1000));
   // set_initial_state(RenderState::make(ShaderAttrib::make_off(), 1000));
   // Backface culling helps eliminating artifacts.
   // Backface culling helps eliminating artifacts.
@@ -57,8 +56,7 @@ LightLensNode(const LightLensNode &copy) :
   Light(copy),
   Light(copy),
   Camera(copy),
   Camera(copy),
   _shadow_caster(copy._shadow_caster),
   _shadow_caster(copy._shadow_caster),
-  _sb_xsize(copy._sb_xsize),
-  _sb_ysize(copy._sb_ysize),
+  _sb_size(copy._sb_size),
   _sb_sort(-10)
   _sb_sort(-10)
 {
 {
 }
 }
@@ -126,8 +124,8 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   Light::write_datagram(manager, dg);
   Light::write_datagram(manager, dg);
 
 
   dg.add_bool(_shadow_caster);
   dg.add_bool(_shadow_caster);
-  dg.add_int32(_sb_xsize);
-  dg.add_int32(_sb_ysize);
+  dg.add_int32(_sb_size[0]);
+  dg.add_int32(_sb_size[1]);
   dg.add_int32(_sb_sort);
   dg.add_int32(_sb_sort);
 }
 }
 
 

+ 9 - 1
panda/src/pgraphnodes/lightLensNode.h

@@ -38,14 +38,22 @@ PUBLISHED:
   INLINE void set_shadow_caster(bool caster);
   INLINE void set_shadow_caster(bool caster);
   INLINE void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10);
   INLINE void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10);
 
 
+  INLINE LVecBase2i get_shadow_buffer_size() const;
+  INLINE void set_shadow_buffer_size(const LVecBase2i &size);
+
   INLINE GraphicsOutputBase *get_shadow_buffer(GraphicsStateGuardianBase *gsg);
   INLINE GraphicsOutputBase *get_shadow_buffer(GraphicsStateGuardianBase *gsg);
 
 
+PUBLISHED:
+  MAKE_PROPERTY(shadow_caster, is_shadow_caster);
+  MAKE_PROPERTY(shadow_buffer_size, get_shadow_buffer_size, set_shadow_buffer_size);
+
 protected:
 protected:
   LightLensNode(const LightLensNode &copy);
   LightLensNode(const LightLensNode &copy);
   void clear_shadow_buffers();
   void clear_shadow_buffers();
 
 
+  LVecBase2i _sb_size;
   bool _shadow_caster;
   bool _shadow_caster;
-  int _sb_xsize, _sb_ysize, _sb_sort;
+  int _sb_sort;
 
 
   // This is really a map of GSG -> GraphicsOutput.
   // This is really a map of GSG -> GraphicsOutput.
   typedef pmap<PT(GraphicsStateGuardianBase), PT(GraphicsOutputBase) > ShadowBuffers;
   typedef pmap<PT(GraphicsStateGuardianBase), PT(GraphicsOutputBase) > ShadowBuffers;

+ 24 - 2
panda/src/pgraphnodes/pointLight.I

@@ -18,6 +18,7 @@ INLINE PointLight::CData::
 CData() :
 CData() :
   _specular_color(1.0f, 1.0f, 1.0f, 1.0f),
   _specular_color(1.0f, 1.0f, 1.0f, 1.0f),
   _attenuation(1.0f, 0.0f, 0.0f),
   _attenuation(1.0f, 0.0f, 0.0f),
+  _max_distance(make_inf((PN_stdfloat)0)),
   _point(0.0f, 0.0f, 0.0f)
   _point(0.0f, 0.0f, 0.0f)
 {
 {
 }
 }
@@ -29,6 +30,7 @@ INLINE PointLight::CData::
 CData(const PointLight::CData &copy) :
 CData(const PointLight::CData &copy) :
   _specular_color(copy._specular_color),
   _specular_color(copy._specular_color),
   _attenuation(copy._attenuation),
   _attenuation(copy._attenuation),
+  _max_distance(copy._max_distance),
   _point(copy._point)
   _point(copy._point)
 {
 {
 }
 }
@@ -88,9 +90,29 @@ set_attenuation(const LVecBase3 &attenuation) {
   cdata->_attenuation = attenuation;
   cdata->_attenuation = attenuation;
 }
 }
 
 
+/**
+ * Returns the maximum distance at which the light has any effect, as previously
+ * specified by set_max_distance.
+ */
+INLINE PN_stdfloat PointLight::
+get_max_distance() const {
+  CDReader cdata(_cycler);
+  return cdata->_max_distance;
+}
+
+/**
+ * Sets the radius of the light's sphere of influence.  Beyond this distance, the
+ * light may be attenuated to zero, if this is supported by the shader.
+ */
+INLINE void PointLight::
+set_max_distance(PN_stdfloat max_distance) {
+  CDWriter cdata(_cycler);
+  cdata->_max_distance = max_distance;
+}
+
 /**
 /**
  * Returns the point in space at which the light is located.  This is local to
  * Returns the point in space at which the light is located.  This is local to
- * the coordinate space in which the light is assigned.
+ * the coordinate space in which the light is assigned, and is usually 0.
  */
  */
 INLINE const LPoint3 &PointLight::
 INLINE const LPoint3 &PointLight::
 get_point() const {
 get_point() const {
@@ -99,7 +121,7 @@ get_point() const {
 }
 }
 
 
 /**
 /**
- * Sets the point in space at which the light is located.
+ * Sets the point in space at which the light is located.  Usually 0.
  */
  */
 INLINE void PointLight::
 INLINE void PointLight::
 set_point(const LPoint3 &point) {
 set_point(const LPoint3 &point) {

+ 13 - 2
panda/src/pgraphnodes/pointLight.cxx

@@ -33,9 +33,12 @@ make_copy() const {
  * Bam file.
  * Bam file.
  */
  */
 void PointLight::CData::
 void PointLight::CData::
-write_datagram(BamWriter *, Datagram &dg) const {
+write_datagram(BamWriter *manager, Datagram &dg) const {
   _specular_color.write_datagram(dg);
   _specular_color.write_datagram(dg);
   _attenuation.write_datagram(dg);
   _attenuation.write_datagram(dg);
+  if (manager->get_file_minor_ver() >= 41) {
+    dg.add_stdfloat(_max_distance);
+  }
   _point.write_datagram(dg);
   _point.write_datagram(dg);
 }
 }
 
 
@@ -44,9 +47,12 @@ write_datagram(BamWriter *, Datagram &dg) const {
  * relevant data from the BamFile for the new Light.
  * relevant data from the BamFile for the new Light.
  */
  */
 void PointLight::CData::
 void PointLight::CData::
-fillin(DatagramIterator &scan, BamReader *) {
+fillin(DatagramIterator &scan, BamReader *manager) {
   _specular_color.read_datagram(scan);
   _specular_color.read_datagram(scan);
   _attenuation.read_datagram(scan);
   _attenuation.read_datagram(scan);
+  if (manager->get_file_minor_ver() >= 41) {
+    _max_distance = scan.get_stdfloat();
+  }
   _point.read_datagram(scan);
   _point.read_datagram(scan);
 }
 }
 
 
@@ -127,6 +133,11 @@ write(ostream &out, int indent_level) const {
   }
   }
   indent(out, indent_level + 2)
   indent(out, indent_level + 2)
     << "attenuation " << get_attenuation() << "\n";
     << "attenuation " << get_attenuation() << "\n";
+
+  if (!cinf(get_max_distance())) {
+    indent(out, indent_level + 2)
+      << "max distance " << get_max_distance() << "\n";
+  }
 }
 }
 
 
 /**
 /**

+ 5 - 0
panda/src/pgraphnodes/pointLight.h

@@ -48,6 +48,10 @@ PUBLISHED:
   INLINE void set_attenuation(const LVecBase3 &attenuation);
   INLINE void set_attenuation(const LVecBase3 &attenuation);
   MAKE_PROPERTY(attenuation, get_attenuation, set_attenuation);
   MAKE_PROPERTY(attenuation, get_attenuation, set_attenuation);
 
 
+  INLINE PN_stdfloat get_max_distance() const;
+  INLINE void set_max_distance(PN_stdfloat max_distance);
+  MAKE_PROPERTY(max_distance, get_max_distance, set_max_distance);
+
   INLINE const LPoint3 &get_point() const;
   INLINE const LPoint3 &get_point() const;
   INLINE void set_point(const LPoint3 &point);
   INLINE void set_point(const LPoint3 &point);
   MAKE_PROPERTY(point, get_point, set_point);
   MAKE_PROPERTY(point, get_point, set_point);
@@ -75,6 +79,7 @@ private:
 
 
     LColor _specular_color;
     LColor _specular_color;
     LVecBase3 _attenuation;
     LVecBase3 _attenuation;
+    PN_stdfloat _max_distance;
     LPoint3 _point;
     LPoint3 _point;
   };
   };
 
 

+ 1 - 0
panda/src/pgraphnodes/shaderGenerator.cxx

@@ -209,6 +209,7 @@ analyze_renderstate(const RenderState *rs) {
   const TransparencyAttrib *transparency;
   const TransparencyAttrib *transparency;
   rs->get_attrib_def(transparency);
   rs->get_attrib_def(transparency);
   if ((transparency->get_mode() == TransparencyAttrib::M_alpha)||
   if ((transparency->get_mode() == TransparencyAttrib::M_alpha)||
+      (transparency->get_mode() == TransparencyAttrib::M_premultiplied_alpha)||
       (transparency->get_mode() == TransparencyAttrib::M_dual)) {
       (transparency->get_mode() == TransparencyAttrib::M_dual)) {
     _have_alpha_blend = true;
     _have_alpha_blend = true;
   }
   }

+ 24 - 2
panda/src/pgraphnodes/spotlight.I

@@ -18,7 +18,8 @@ INLINE Spotlight::CData::
 CData() :
 CData() :
   _exponent(50.0f),
   _exponent(50.0f),
   _specular_color(1.0f, 1.0f, 1.0f, 1.0f),
   _specular_color(1.0f, 1.0f, 1.0f, 1.0f),
-  _attenuation(1.0f, 0.0f, 0.0f)
+  _attenuation(1.0f, 0.0f, 0.0f),
+  _max_distance(make_inf((PN_stdfloat)0))
 {
 {
 }
 }
 
 
@@ -29,7 +30,8 @@ INLINE Spotlight::CData::
 CData(const Spotlight::CData &copy) :
 CData(const Spotlight::CData &copy) :
   _exponent(copy._exponent),
   _exponent(copy._exponent),
   _specular_color(copy._specular_color),
   _specular_color(copy._specular_color),
-  _attenuation(copy._attenuation)
+  _attenuation(copy._attenuation),
+  _max_distance(copy._max_distance)
 {
 {
 }
 }
 
 
@@ -111,3 +113,23 @@ set_attenuation(const LVecBase3 &attenuation) {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
   cdata->_attenuation = attenuation;
   cdata->_attenuation = attenuation;
 }
 }
+
+/**
+ * Returns the maximum distance at which the light has any effect, as previously
+ * specified by set_max_distance.
+ */
+INLINE PN_stdfloat Spotlight::
+get_max_distance() const {
+  CDReader cdata(_cycler);
+  return cdata->_max_distance;
+}
+
+/**
+ * Sets the radius of the light's sphere of influence.  Beyond this distance, the
+ * light may be attenuated to zero, if this is supported by the shader.
+ */
+INLINE void Spotlight::
+set_max_distance(PN_stdfloat max_distance) {
+  CDWriter cdata(_cycler);
+  cdata->_max_distance = max_distance;
+}

+ 13 - 2
panda/src/pgraphnodes/spotlight.cxx

@@ -37,10 +37,13 @@ make_copy() const {
  * Bam file.
  * Bam file.
  */
  */
 void Spotlight::CData::
 void Spotlight::CData::
-write_datagram(BamWriter *, Datagram &dg) const {
+write_datagram(BamWriter *manager, Datagram &dg) const {
   dg.add_stdfloat(_exponent);
   dg.add_stdfloat(_exponent);
   _specular_color.write_datagram(dg);
   _specular_color.write_datagram(dg);
   _attenuation.write_datagram(dg);
   _attenuation.write_datagram(dg);
+  if (manager->get_file_minor_ver() >= 41) {
+    dg.add_stdfloat(_max_distance);
+  }
 }
 }
 
 
 /**
 /**
@@ -48,10 +51,13 @@ write_datagram(BamWriter *, Datagram &dg) const {
  * relevant data from the BamFile for the new Light.
  * relevant data from the BamFile for the new Light.
  */
  */
 void Spotlight::CData::
 void Spotlight::CData::
-fillin(DatagramIterator &scan, BamReader *) {
+fillin(DatagramIterator &scan, BamReader *manager) {
   _exponent = scan.get_stdfloat();
   _exponent = scan.get_stdfloat();
   _specular_color.read_datagram(scan);
   _specular_color.read_datagram(scan);
   _attenuation.read_datagram(scan);
   _attenuation.read_datagram(scan);
+  if (manager->get_file_minor_ver() >= 41) {
+    _max_distance = scan.get_stdfloat();
+  }
 }
 }
 
 
 /**
 /**
@@ -113,6 +119,11 @@ write(ostream &out, int indent_level) const {
   indent(out, indent_level + 2)
   indent(out, indent_level + 2)
     << "exponent " << get_exponent() << "\n";
     << "exponent " << get_exponent() << "\n";
 
 
+  if (!cinf(get_max_distance())) {
+    indent(out, indent_level + 2)
+      << "max distance " << get_max_distance() << "\n";
+  }
+
   Lens *lens = get_lens();
   Lens *lens = get_lens();
   if (lens != (Lens *)NULL) {
   if (lens != (Lens *)NULL) {
     lens->write(out, indent_level + 2);
     lens->write(out, indent_level + 2);

+ 5 - 0
panda/src/pgraphnodes/spotlight.h

@@ -59,6 +59,10 @@ PUBLISHED:
   INLINE void set_attenuation(const LVecBase3 &attenuation);
   INLINE void set_attenuation(const LVecBase3 &attenuation);
   MAKE_PROPERTY(attenuation, get_attenuation, set_attenuation);
   MAKE_PROPERTY(attenuation, get_attenuation, set_attenuation);
 
 
+  INLINE PN_stdfloat get_max_distance() const;
+  INLINE void set_max_distance(PN_stdfloat max_distance);
+  MAKE_PROPERTY(max_distance, get_max_distance, set_max_distance);
+
   virtual int get_class_priority() const;
   virtual int get_class_priority() const;
 
 
   static PT(Texture) make_spot(int pixel_width, PN_stdfloat full_radius,
   static PT(Texture) make_spot(int pixel_width, PN_stdfloat full_radius,
@@ -92,6 +96,7 @@ private:
     PN_stdfloat _exponent;
     PN_stdfloat _exponent;
     LColor _specular_color;
     LColor _specular_color;
     LVecBase3 _attenuation;
     LVecBase3 _attenuation;
+    PN_stdfloat _max_distance;
   };
   };
 
 
   PipelineCycler<CData> _cycler;
   PipelineCycler<CData> _cycler;

+ 1 - 1
panda/src/physx/physxClothDesc.cxx

@@ -281,11 +281,11 @@ get_solver_iterations() const {
   return _desc.solverIterations;
   return _desc.solverIterations;
 }
 }
 
 
-/*
 /**
 /**
  * Used by PhysScene to query the sizes of arrays to allocate for the user
  * Used by PhysScene to query the sizes of arrays to allocate for the user
  * buffers in PhysxClothNode.
  * buffers in PhysxClothNode.
  */
  */
+/*
 void PhysxClothDesc::
 void PhysxClothDesc::
 get_mesh_numbers(NxU32 &numVertices, NxU32 &numTriangles) {
 get_mesh_numbers(NxU32 &numVertices, NxU32 &numTriangles) {
 
 

+ 31 - 7
panda/src/physx/physxSoftBody.cxx

@@ -535,16 +535,10 @@ get_hard_stretch_limitation_factor() const {
 #endif // NX_SDK_VERSION_NUMBER > 281
 #endif // NX_SDK_VERSION_NUMBER > 281
 
 
 
 
-
-
-
-
-
-
-/*
 /**
 /**
  * Attaches a cloth vertex to a position in world space.
  * Attaches a cloth vertex to a position in world space.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 attach_vertex_to_global_pos(unsigned int vertexId, LPoint3f const &pos) {
 attach_vertex_to_global_pos(unsigned int vertexId, LPoint3f const &pos) {
 
 
@@ -553,6 +547,7 @@ attach_vertex_to_global_pos(unsigned int vertexId, LPoint3f const &pos) {
 
 
   _ptr->attachVertexToGlobalPosition(vertexId, PhysxManager::point3_to_nxVec3(pos));
   _ptr->attachVertexToGlobalPosition(vertexId, PhysxManager::point3_to_nxVec3(pos));
 }
 }
+*/
 
 
 /**
 /**
  * Attaches the cloth to a shape.  All cloth points currently inside the shape
  * Attaches the cloth to a shape.  All cloth points currently inside the shape
@@ -561,6 +556,7 @@ attach_vertex_to_global_pos(unsigned int vertexId, LPoint3f const &pos) {
  * This method only works with primitive and convex shapes.  Since the inside
  * This method only works with primitive and convex shapes.  Since the inside
  * of a general triangle mesh is not clearly defined.
  * of a general triangle mesh is not clearly defined.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 attach_to_shape(PhysxShape *shape) {
 attach_to_shape(PhysxShape *shape) {
 
 
@@ -570,6 +566,7 @@ attach_to_shape(PhysxShape *shape) {
   NxU32 attachmentFlags = 0; // --TODO--
   NxU32 attachmentFlags = 0; // --TODO--
   _ptr->attachToShape(shape->ptr(), attachmentFlags);
   _ptr->attachToShape(shape->ptr(), attachmentFlags);
 }
 }
+*/
 
 
 /**
 /**
  * Attaches the cloth to all shapes, currently colliding.
  * Attaches the cloth to all shapes, currently colliding.
@@ -577,6 +574,7 @@ attach_to_shape(PhysxShape *shape) {
  * This method only works with primitive and convex shapes.  Since the inside
  * This method only works with primitive and convex shapes.  Since the inside
  * of a general triangle mesh is not clearly defined.
  * of a general triangle mesh is not clearly defined.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 attach_to_colliding_shapes() {
 attach_to_colliding_shapes() {
 
 
@@ -585,6 +583,7 @@ attach_to_colliding_shapes() {
   NxU32 attachmentFlags = 0; // --TODO--
   NxU32 attachmentFlags = 0; // --TODO--
   _ptr->attachToCollidingShapes(attachmentFlags);
   _ptr->attachToCollidingShapes(attachmentFlags);
 }
 }
+*/
 
 
 /**
 /**
  * Detaches the cloth from a shape it has been attached to before.
  * Detaches the cloth from a shape it has been attached to before.
@@ -592,6 +591,7 @@ attach_to_colliding_shapes() {
  * If the cloth has not been attached to the shape before, the call has no
  * If the cloth has not been attached to the shape before, the call has no
  * effect.
  * effect.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 detach_from_shape(PhysxShape *shape) {
 detach_from_shape(PhysxShape *shape) {
 
 
@@ -600,20 +600,24 @@ detach_from_shape(PhysxShape *shape) {
 
 
   _ptr->detachFromShape(shape->ptr());
   _ptr->detachFromShape(shape->ptr());
 }
 }
+*/
 
 
 /**
 /**
  * Frees a previously attached cloth point.
  * Frees a previously attached cloth point.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 free_vertex(unsigned int vertexId) {
 free_vertex(unsigned int vertexId) {
 
 
   nassertv(_error_type == ET_ok);
   nassertv(_error_type == ET_ok);
   _ptr->freeVertex(vertexId);
   _ptr->freeVertex(vertexId);
 }
 }
+*/
 
 
 /**
 /**
  * Attaches a cloth vertex to a local position within a shape.
  * Attaches a cloth vertex to a local position within a shape.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 attach_vertex_to_shape(unsigned int vertexId, PhysxShape *shape, LPoint3f const &localPos) {
 attach_vertex_to_shape(unsigned int vertexId, PhysxShape *shape, LPoint3f const &localPos) {
 
 
@@ -626,10 +630,12 @@ attach_vertex_to_shape(unsigned int vertexId, PhysxShape *shape, LPoint3f const
                             PhysxManager::point3_to_nxVec3(localPos),
                             PhysxManager::point3_to_nxVec3(localPos),
                             attachmentFlags);
                             attachmentFlags);
 }
 }
+*/
 
 
 /**
 /**
  * Return the attachment status of the given vertex.
  * Return the attachment status of the given vertex.
  */
  */
+/*
 PhysxEnums::PhysxVertexAttachmentStatus PhysxSoftBody::
 PhysxEnums::PhysxVertexAttachmentStatus PhysxSoftBody::
 get_vertex_attachment_status(unsigned int vertexId) const {
 get_vertex_attachment_status(unsigned int vertexId) const {
 
 
@@ -638,12 +644,14 @@ get_vertex_attachment_status(unsigned int vertexId) const {
 
 
   return (PhysxVertexAttachmentStatus) _ptr->getVertexAttachmentStatus(vertexId);
   return (PhysxVertexAttachmentStatus) _ptr->getVertexAttachmentStatus(vertexId);
 }
 }
+*/
 
 
 /**
 /**
  * Returns the pointer to an attached shape pointer of the given vertex.  If
  * Returns the pointer to an attached shape pointer of the given vertex.  If
  * the vertex is not attached or attached to a global position, NULL is
  * the vertex is not attached or attached to a global position, NULL is
  * returned.
  * returned.
  */
  */
+/*
 PhysxShape *PhysxSoftBody::
 PhysxShape *PhysxSoftBody::
 get_vertex_attachment_shape(unsigned int vertexId) const {
 get_vertex_attachment_shape(unsigned int vertexId) const {
 
 
@@ -655,12 +663,14 @@ get_vertex_attachment_shape(unsigned int vertexId) const {
 
 
   return shape;
   return shape;
 }
 }
+*/
 
 
 /**
 /**
  * Returns the attachment position of the given vertex.  If the vertex is
  * Returns the attachment position of the given vertex.  If the vertex is
  * attached to shape, the position local to the shape's pose is returned.  If
  * attached to shape, the position local to the shape's pose is returned.  If
  * the vertex is not attached, the return value is undefined.
  * the vertex is not attached, the return value is undefined.
  */
  */
+/*
 LPoint3f PhysxSoftBody::
 LPoint3f PhysxSoftBody::
 get_vertex_attachment_pos(unsigned int vertexId) const {
 get_vertex_attachment_pos(unsigned int vertexId) const {
 
 
@@ -670,11 +680,13 @@ get_vertex_attachment_pos(unsigned int vertexId) const {
 
 
   return PhysxManager::nxVec3_to_point3(_ptr->getVertexAttachmentPosition(vertexId));
   return PhysxManager::nxVec3_to_point3(_ptr->getVertexAttachmentPosition(vertexId));
 }
 }
+*/
 
 
 /**
 /**
  * Sets an external acceleration which affects all non attached particles of
  * Sets an external acceleration which affects all non attached particles of
  * the cloth.
  * the cloth.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 set_external_acceleration(LVector3f const &acceleration) {
 set_external_acceleration(LVector3f const &acceleration) {
 
 
@@ -683,10 +695,12 @@ set_external_acceleration(LVector3f const &acceleration) {
 
 
   _ptr->setExternalAcceleration(PhysxManager::vec3_to_nxVec3(acceleration));
   _ptr->setExternalAcceleration(PhysxManager::vec3_to_nxVec3(acceleration));
 }
 }
+*/
 
 
 /**
 /**
  * Sets an acceleration acting normal to the cloth surface at each vertex.
  * Sets an acceleration acting normal to the cloth surface at each vertex.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 set_wind_acceleration(LVector3f const &acceleration) {
 set_wind_acceleration(LVector3f const &acceleration) {
 
 
@@ -695,33 +709,39 @@ set_wind_acceleration(LVector3f const &acceleration) {
 
 
   _ptr->setWindAcceleration(PhysxManager::vec3_to_nxVec3(acceleration));
   _ptr->setWindAcceleration(PhysxManager::vec3_to_nxVec3(acceleration));
 }
 }
+*/
 
 
 /**
 /**
  * Retrieves the external acceleration which affects all non attached
  * Retrieves the external acceleration which affects all non attached
  * particles of the cloth.
  * particles of the cloth.
  */
  */
+/*
 LVector3f PhysxSoftBody::
 LVector3f PhysxSoftBody::
 get_external_acceleration() const {
 get_external_acceleration() const {
 
 
   nassertr(_error_type == ET_ok, LVector3f::zero());
   nassertr(_error_type == ET_ok, LVector3f::zero());
   return PhysxManager::nxVec3_to_vec3(_ptr->getExternalAcceleration());
   return PhysxManager::nxVec3_to_vec3(_ptr->getExternalAcceleration());
 }
 }
+*/
 
 
 /**
 /**
  * Retrieves the acceleration acting normal to the cloth surface at each
  * Retrieves the acceleration acting normal to the cloth surface at each
  * vertex
  * vertex
  */
  */
+/*
 LVector3f PhysxSoftBody::
 LVector3f PhysxSoftBody::
 get_wind_acceleration() const {
 get_wind_acceleration() const {
 
 
   nassertr(_error_type == ET_ok, LVector3f::zero());
   nassertr(_error_type == ET_ok, LVector3f::zero());
   return PhysxManager::nxVec3_to_vec3(_ptr->getWindAcceleration());
   return PhysxManager::nxVec3_to_vec3(_ptr->getWindAcceleration());
 }
 }
+*/
 
 
 /**
 /**
  * Applies a force (or impulse) defined in the global coordinate frame, to a
  * Applies a force (or impulse) defined in the global coordinate frame, to a
  * particular vertex of the cloth.
  * particular vertex of the cloth.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 add_force_at_vertex(LVector3f const &force, int vertexId, PhysxForceMode mode) {
 add_force_at_vertex(LVector3f const &force, int vertexId, PhysxForceMode mode) {
 
 
@@ -730,11 +750,13 @@ add_force_at_vertex(LVector3f const &force, int vertexId, PhysxForceMode mode) {
                          vertexId,
                          vertexId,
                          (NxForceMode) mode);
                          (NxForceMode) mode);
 }
 }
+*/
 
 
 /**
 /**
  * Applies a radial force (or impulse) at a particular position.  All vertices
  * Applies a radial force (or impulse) at a particular position.  All vertices
  * within radius will be affected with a quadratic drop-off.
  * within radius will be affected with a quadratic drop-off.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 add_force_at_pos(LPoint3f const &pos, float magnitude, float radius, PhysxForceMode mode) {
 add_force_at_pos(LPoint3f const &pos, float magnitude, float radius, PhysxForceMode mode) {
 
 
@@ -744,11 +766,13 @@ add_force_at_pos(LPoint3f const &pos, float magnitude, float radius, PhysxForceM
                       radius,
                       radius,
                       (NxForceMode) mode);
                       (NxForceMode) mode);
 }
 }
+*/
 
 
 /**
 /**
  * Applies a directed force (or impulse) at a particular position.  All
  * Applies a directed force (or impulse) at a particular position.  All
  * vertices within radius will be affected with a quadratic drop-off.
  * vertices within radius will be affected with a quadratic drop-off.
  */
  */
+/*
 void PhysxSoftBody::
 void PhysxSoftBody::
 add_directed_force_at_pos(LPoint3f const &pos, LVector3f const &force, float radius, PhysxForceMode mode) {
 add_directed_force_at_pos(LPoint3f const &pos, LVector3f const &force, float radius, PhysxForceMode mode) {
 
 

+ 1 - 1
panda/src/physx/physxSoftBodyDesc.cxx

@@ -317,11 +317,11 @@ get_solver_iterations() const {
   return _desc.solverIterations;
   return _desc.solverIterations;
 }
 }
 
 
-/*
 /**
 /**
  * Used by PhysScene to query the sizes of arrays to allocate for the user
  * Used by PhysScene to query the sizes of arrays to allocate for the user
  * buffers in PhysxSoftBodyNode.
  * buffers in PhysxSoftBodyNode.
  */
  */
+/*
 void PhysxSoftBodyDesc::
 void PhysxSoftBodyDesc::
 get_mesh_numbers(NxU32 &numVertices, NxU32 &numTriangles) {
 get_mesh_numbers(NxU32 &numVertices, NxU32 &numTriangles) {
 
 

+ 4 - 2
panda/src/physx/physxVehicle.cxx

@@ -57,10 +57,10 @@ update_vehicle(float dt) {
   // TODO !!!
   // TODO !!!
 }
 }
 
 
-/*
 /**
 /**
  * Returns the actor for this vehicle.
  * Returns the actor for this vehicle.
  */
  */
+/*
 PhysxActor *PhysxVehicle::
 PhysxActor *PhysxVehicle::
 get_actor() const {
 get_actor() const {
 
 
@@ -69,20 +69,22 @@ get_actor() const {
 }
 }
 */
 */
 
 
-/*
 /**
 /**
  * Returns the number of wheels on this vehicle.
  * Returns the number of wheels on this vehicle.
  */
  */
+/*
 unsigned int PhysxVehicle::
 unsigned int PhysxVehicle::
 get_num_wheels() const {
 get_num_wheels() const {
 
 
   nassertr(_error_type == ET_ok, 0);
   nassertr(_error_type == ET_ok, 0);
   return _wheels.size();
   return _wheels.size();
 }
 }
+*/
 
 
 /**
 /**
  * Returns the n-th wheel of this vehicle.
  * Returns the n-th wheel of this vehicle.
  */
  */
+/*
 PhysxWheel *PhysxVehicle::
 PhysxWheel *PhysxVehicle::
 get_wheel(unsigned int idx) const {
 get_wheel(unsigned int idx) const {
 
 

+ 4 - 3
panda/src/physx/physxVehicle.h

@@ -34,10 +34,11 @@ PUBLISHED:
   INLINE PhysxVehicle();
   INLINE PhysxVehicle();
   INLINE ~PhysxVehicle();
   INLINE ~PhysxVehicle();
 
 
-  // PhysxActor *get_actor() const;
+  //PhysxActor *get_actor() const;
 
 
-  // unsigned int get_num_wheels() const; PhysxWheel *get_wheel(unsigned int
-  // idx) const; MAKE_SEQ(get_wheels, get_num_wheels, get_wheel);
+  //unsigned int get_num_wheels() const;
+  //PhysxWheel *get_wheel(unsigned int idx) const;
+  //MAKE_SEQ(get_wheels, get_num_wheels, get_wheel);
 
 
   INLINE void ls() const;
   INLINE void ls() const;
   INLINE void ls(ostream &out, int indent_level=0) const;
   INLINE void ls(ostream &out, int indent_level=0) const;

+ 1 - 1
panda/src/physx/physxVehicleDesc.cxx

@@ -13,10 +13,10 @@
 
 
 #include "physxVehicleDesc.h"
 #include "physxVehicleDesc.h"
 
 
-/*
 /**
 /**
  *
  *
  */
  */
+/*
 void PhysxVehicleDesc::
 void PhysxVehicleDesc::
 add_wheel(PhysxWheelDesc *wheelDesc) {
 add_wheel(PhysxWheelDesc *wheelDesc) {
 
 

+ 6 - 2
panda/src/physx/physxWheel.cxx

@@ -17,10 +17,10 @@
 
 
 TypeHandle PhysxWheel::_type_handle;
 TypeHandle PhysxWheel::_type_handle;
 
 
-/*
 /**
 /**
  *
  *
  */
  */
+/*
 PhysxWheelShape *PhysxWheel::
 PhysxWheelShape *PhysxWheel::
 get_wheel_shape() const {
 get_wheel_shape() const {
 
 
@@ -28,7 +28,6 @@ get_wheel_shape() const {
 }
 }
 */
 */
 
 
-/*
 /**
 /**
  * Attaches a node path to this wheel.  The node path's transform will be
  * Attaches a node path to this wheel.  The node path's transform will be
  * updated automatically.
  * updated automatically.
@@ -36,6 +35,7 @@ get_wheel_shape() const {
  * Note: any non-uniform scale or shear set on the NodePath's transform will
  * Note: any non-uniform scale or shear set on the NodePath's transform will
  * be overwritten at the time of the first update.
  * be overwritten at the time of the first update.
  */
  */
+/*
 void PhysxWheel::
 void PhysxWheel::
 attach_node_path(const NodePath &np) {
 attach_node_path(const NodePath &np) {
 
 
@@ -43,22 +43,26 @@ attach_node_path(const NodePath &np) {
   nassertv_always(!np.is_empty());
   nassertv_always(!np.is_empty());
   _np = NodePath(np);
   _np = NodePath(np);
 }
 }
+*/
 
 
 /**
 /**
  * Detaches a previously assigned NodePath from this wheel.  The NodePath's
  * Detaches a previously assigned NodePath from this wheel.  The NodePath's
  * transform will no longer be updated.
  * transform will no longer be updated.
  */
  */
+/*
 void PhysxWheel::
 void PhysxWheel::
 detach_node_path() {
 detach_node_path() {
 
 
   nassertv(_error_type == ET_ok);
   nassertv(_error_type == ET_ok);
   _np = NodePath();
   _np = NodePath();
 }
 }
+*/
 
 
 /**
 /**
  * Retrieves a previously attached NodePath.  An empty NodePath will be
  * Retrieves a previously attached NodePath.  An empty NodePath will be
  * returned if no NodePath has been attached to this wheel.
  * returned if no NodePath has been attached to this wheel.
  */
  */
+/*
 NodePath PhysxWheel::
 NodePath PhysxWheel::
 get_node_path() const {
 get_node_path() const {
 
 

+ 5 - 6
panda/src/physx/physxWheel.h

@@ -32,17 +32,16 @@ PUBLISHED:
   INLINE PhysxWheel();
   INLINE PhysxWheel();
   INLINE ~PhysxWheel();
   INLINE ~PhysxWheel();
 
 
-  // PhysxActor *get_touched_actor() const; PhysxWheelShape *get_wheel_shape()
-  // const;
+  //PhysxActor *get_touched_actor() const;
+  //PhysxWheelShape *get_wheel_shape() const;
 
 
-  // void attach_node_path(const NodePath &np); void detach_node_path();
-  // NodePath get_node_path() const;
+  //void attach_node_path(const NodePath &np);
+  //void detach_node_path();
+  //NodePath get_node_path() const;
 
 
   INLINE void ls() const;
   INLINE void ls() const;
   INLINE void ls(ostream &out, int indent_level=0) const;
   INLINE void ls(ostream &out, int indent_level=0) const;
 
 
-public:
-
 private:
 private:
   PT(PhysxWheelShape) _wheelShape;
   PT(PhysxWheelShape) _wheelShape;
   NodePath _np;
   NodePath _np;

+ 1 - 1
panda/src/physx/physxWheelDesc.cxx

@@ -13,10 +13,10 @@
 
 
 #include "physxWheelDesc.h"
 #include "physxWheelDesc.h"
 
 
-/*
 /**
 /**
  *
  *
  */
  */
+/*
 void PhysxWheelDesc::
 void PhysxWheelDesc::
 set_wheel_radius(float wheelRadius) {
 set_wheel_radius(float wheelRadius) {
 
 

+ 20 - 13
panda/src/pipeline/pipeline.cxx

@@ -24,22 +24,23 @@ Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL;
  */
  */
 Pipeline::
 Pipeline::
 Pipeline(const string &name, int num_stages) :
 Pipeline(const string &name, int num_stages) :
-  Namable(name)
+  Namable(name),
 #ifdef THREADED_PIPELINE
 #ifdef THREADED_PIPELINE
-  , _lock("Pipeline")
+  _num_stages(num_stages),
+  _lock("Pipeline")
+#else
+  _num_stages(1)
 #endif
 #endif
 {
 {
 #ifdef THREADED_PIPELINE
 #ifdef THREADED_PIPELINE
-
-/*
- * We maintain all of the cyclers in the world on one of two linked lists.
- * Cyclers that are "clean", which is to say, they have the same value across
- * all pipeline stages, are stored on the _clean list.  Cyclers that are
- * "dirty", which have different values across some pipeline stages, are
- * stored instead on the _dirty list.  Cyclers can move themselves from clean
- * to dirty by calling add_dirty_cycler(), and cyclers get moved from dirty to
- * clean during cycle().
- */
+  // We maintain all of the cyclers in the world on one of two linked
+  // lists.  Cyclers that are "clean", which is to say, they have the
+  // same value across all pipeline stages, are stored on the _clean
+  // list.  Cyclers that are "dirty", which have different values
+  // across some pipeline stages, are stored instead on the _dirty
+  // list.  Cyclers can move themselves from clean to dirty by calling
+  // add_dirty_cycler(), and cyclers get moved from dirty to clean
+  // during cycle().
 
 
   // To visit each cycler once requires traversing both lists.
   // To visit each cycler once requires traversing both lists.
   _clean.make_head();
   _clean.make_head();
@@ -53,9 +54,15 @@ Pipeline(const string &name, int num_stages) :
   // This flag is true only during the call to cycle().
   // This flag is true only during the call to cycle().
   _cycling = false;
   _cycling = false;
 
 
+#else
+  if (num_stages != 1) {
+    pipeline_cat.warning()
+      << "Requested " << num_stages
+      << " pipeline stages but multithreaded render pipelines not enabled in build.\n";
+  }
 #endif  // THREADED_PIPELINE
 #endif  // THREADED_PIPELINE
 
 
-  set_num_stages(num_stages);
+  nassertv(num_stages >= 1);
 }
 }
 
 
 /**
 /**

+ 115 - 6
panda/src/pnmimage/pfmFile.cxx

@@ -1211,14 +1211,65 @@ void PfmFile::
 xform(const LMatrix4f &transform) {
 xform(const LMatrix4f &transform) {
   nassertv(is_valid());
   nassertv(is_valid());
 
 
-  for (int yi = 0; yi < _y_size; ++yi) {
-    for (int xi = 0; xi < _x_size; ++xi) {
-      if (!has_point(xi, yi)) {
-        continue;
+  int num_channels = get_num_channels();
+  switch (num_channels) {
+  case 1:
+    {
+      for (int yi = 0; yi < _y_size; ++yi) {
+        for (int xi = 0; xi < _x_size; ++xi) {
+          if (!has_point(xi, yi)) {
+            continue;
+          }
+          PN_float32 pi = get_point1(xi, yi);
+          LPoint3f po = transform.xform_point(LPoint3f(pi, 0.0, 0.0));
+          set_point1(xi, yi, po[0]);
+        }
+      }
+    }
+    break;
+
+  case 2:
+    {
+      for (int yi = 0; yi < _y_size; ++yi) {
+        for (int xi = 0; xi < _x_size; ++xi) {
+          if (!has_point(xi, yi)) {
+            continue;
+          }
+          LPoint2f pi = get_point2(xi, yi);
+          LPoint3f po = transform.xform_point(LPoint3f(pi[0], pi[1], 0.0));
+          set_point2(xi, yi, LPoint2f(po[0], po[1]));
+        }
+      }
+    }
+    break;
+
+  case 3:
+    {
+      for (int yi = 0; yi < _y_size; ++yi) {
+        for (int xi = 0; xi < _x_size; ++xi) {
+          if (!has_point(xi, yi)) {
+            continue;
+          }
+          LPoint3f &p = modify_point3(xi, yi);
+          transform.xform_point_general_in_place(p);
+        }
       }
       }
-      LPoint3f &p = modify_point(xi, yi);
-      transform.xform_point_general_in_place(p);
     }
     }
+    break;
+
+  case 4:
+    {
+      for (int yi = 0; yi < _y_size; ++yi) {
+        for (int xi = 0; xi < _x_size; ++xi) {
+          if (!has_point(xi, yi)) {
+            continue;
+          }
+          LPoint4f &p = modify_point4(xi, yi);
+          transform.xform_in_place(p);
+        }
+      }
+    }
+    break;
   }
   }
 }
 }
 
 
@@ -2158,6 +2209,64 @@ operator *= (float multiplier) {
   }
   }
 }
 }
 
 
+/**
+ * index_image is a WxH 1-channel image, while pixel_values is an Nx1
+ * image with any number of channels.  Typically pixel_values will be
+ * a 256x1 image.
+ *
+ * Fills the PfmFile with a new image the same width and height as
+ * index_image, with the same number of channels as pixel_values.
+ *
+ * Each pixel of the new image is computed with the formula:
+ *
+ * new_image(x, y) = pixel_values(index_image(x, y)[channel], 0)
+ *
+ * At present, no interpolation is performed; the nearest value in
+ * pixel_values is discovered.  This may change in the future.
+ */
+void PfmFile::
+indirect_1d_lookup(const PfmFile &index_image, int channel,
+                   const PfmFile &pixel_values) {
+  clear(index_image.get_x_size(), index_image.get_y_size(),
+        pixel_values.get_num_channels());
+
+  for (int yi = 0; yi < get_y_size(); ++yi) {
+    switch (get_num_channels()) {
+    case 1:
+      for (int xi = 0; xi < get_x_size(); ++xi) {
+        int v = int(index_image.get_channel(xi, yi, channel) * (pixel_values.get_x_size() - 1) + 0.5);
+        nassertv(v >= 0 && v < pixel_values.get_x_size());
+        set_point1(xi, yi, pixel_values.get_point1(v, 0));
+      }
+      break;
+
+    case 2:
+      for (int xi = 0; xi < get_x_size(); ++xi) {
+        int v = int(index_image.get_channel(xi, yi, channel) * (pixel_values.get_x_size() - 1) + 0.5);
+        nassertv(v >= 0 && v < pixel_values.get_x_size());
+        set_point2(xi, yi, pixel_values.get_point2(v, 0));
+      }
+      break;
+
+    case 3:
+      for (int xi = 0; xi < get_x_size(); ++xi) {
+        int v = int(index_image.get_channel(xi, yi, channel) * (pixel_values.get_x_size() - 1) + 0.5);
+        nassertv(v >= 0 && v < pixel_values.get_x_size());
+        set_point3(xi, yi, pixel_values.get_point3(v, 0));
+      }
+      break;
+
+    case 4:
+      for (int xi = 0; xi < get_x_size(); ++xi) {
+        int v = int(index_image.get_channel(xi, yi, channel) * (pixel_values.get_x_size() - 1) + 0.5);
+        nassertv(v >= 0 && v < pixel_values.get_x_size());
+        set_point4(xi, yi, pixel_values.get_point4(v, 0));
+      }
+      break;
+    }
+  }
+}
+
 /**
 /**
  * Adjusts each channel of the image by raising the corresponding component
  * Adjusts each channel of the image by raising the corresponding component
  * value to the indicated exponent, such that L' = L ^ exponent.
  * value to the indicated exponent, such that L' = L ^ exponent.

+ 3 - 0
panda/src/pnmimage/pfmFile.h

@@ -158,6 +158,9 @@ PUBLISHED:
 
 
   void operator *= (float multiplier);
   void operator *= (float multiplier);
 
 
+  void indirect_1d_lookup(const PfmFile &index_image, int channel,
+                          const PfmFile &pixel_values);
+
   INLINE void gamma_correct(float from_gamma, float to_gamma);
   INLINE void gamma_correct(float from_gamma, float to_gamma);
   INLINE void gamma_correct_alpha(float from_gamma, float to_gamma);
   INLINE void gamma_correct_alpha(float from_gamma, float to_gamma);
   INLINE void apply_exponent(float gray_exponent);
   INLINE void apply_exponent(float gray_exponent);

+ 2 - 2
panda/src/pnmimage/pnmImage.cxx

@@ -1667,8 +1667,8 @@ fill_distance_outside(const PNMImage &mask, float threshold, int radius) {
  *
  *
  * new_image(x, y) = pixel_values(index_image(x, y)[channel], 0)
  * new_image(x, y) = pixel_values(index_image(x, y)[channel], 0)
  *
  *
- * No interpolation is performed; the nearest value in pixel_values is
- * discovered.
+ * At present, no interpolation is performed; the nearest value in
+ * pixel_values is discovered.  This may change in the future.
  */
  */
 void PNMImage::
 void PNMImage::
 indirect_1d_lookup(const PNMImage &index_image, int channel,
 indirect_1d_lookup(const PNMImage &index_image, int channel,

+ 2 - 1
panda/src/pnmtext/pnmTextMaker.cxx

@@ -16,6 +16,8 @@
 #include "filename.h"
 #include "filename.h"
 #include "pnmImage.h"
 #include "pnmImage.h"
 
 
+#include FT_OUTLINE_H
+
 /**
 /**
  * The constructor expects the name of some font file that FreeType can read,
  * The constructor expects the name of some font file that FreeType can read,
  * along with face_index, indicating which font within the file to load
  * along with face_index, indicating which font within the file to load
@@ -193,7 +195,6 @@ make_glyph(int glyph_index) {
 
 
       PN_stdfloat tex_x_size, tex_y_size, tex_x_orig, tex_y_orig;
       PN_stdfloat tex_x_size, tex_y_size, tex_x_orig, tex_y_orig;
       FT_BBox bounds;
       FT_BBox bounds;
-      TransparencyAttrib::Mode alpha_mode;
 
 
       // Calculate suitable texture dimensions for the signed distance field.
       // Calculate suitable texture dimensions for the signed distance field.
       // This is the same calculation that Freetype uses in its bitmap
       // This is the same calculation that Freetype uses in its bitmap

+ 34 - 35
panda/src/putil/bam.h

@@ -25,42 +25,41 @@
 static const string _bam_header = string("pbj\0\n\r", 6);
 static const string _bam_header = string("pbj\0\n\r", 6);
 
 
 static const unsigned short _bam_major_ver = 6;
 static const unsigned short _bam_major_ver = 6;
-// Bumped to major version 2 on 7600 due to major changes in Character.
-// Bumped to major version 3 on 12800 to change float64's to float32's.
-// Bumped to major version 4 on 41002 to store new scene graph.  Bumped to
-// major version 5 on 5605 for new Geom implementation.  Bumped to major
-// version 6 on 21106 to factor out PandaNode::CData.
+// Bumped to major version 2 on 2000-07-06 due to major changes in Character.
+// Bumped to major version 3 on 2000-12-08 to change float64's to float32's.
+// Bumped to major version 4 on 2002-04-10 to store new scene graph.
+// Bumped to major version 5 on 2005-05-06 for new Geom implementation.
+// Bumped to major version 6 on 2006-02-11 to factor out PandaNode::CData.
 
 
 static const unsigned short _bam_first_minor_ver = 14;
 static const unsigned short _bam_first_minor_ver = 14;
-static const unsigned short _bam_minor_ver = 40;
-/*
- * Bumped to minor version 14 on 121907 to change default ColorAttrib.  Bumped
- * to minor version 15 on 4908 to add TextureAttrib::_implicit_sort.  Bumped
- * to minor version 16 on 51308 to add Texture::_quality_level.  Bumped to
- * minor version 17 on 8608 to add PartBundle::_anim_preload.  Bumped to minor
- * version 18 on 81408 to add Texture::_simple_ram_image.  Bumped to minor
- * version 19 on 81408 to add PandaNode::_bounds_type.  Bumped to minor
- * version 20 on 42109 to add MovingPartBase::_forced_channel.  Bumped to
- * minor version 21 on 22608 to add BamEnums::BamObjectCode.  Bumped to minor
- * version 22 on 73109 to add UvScrollNode R speed.  Bumped to minor version
- * 23 on 5410 to add internal TextureAttrib overrides.  Bumped to minor
- * version 24 on 5410 to add internal TexMatrixAttrib overrides.  Bumped to
- * minor version 25 on 62211 to add support for caching movie files.  Bumped
- * to minor version 26 on 8511 to add multiview (stereo) Textures.  Bumped to
- * minor version 27 on 10911 to add stdfloat_double.  Bumped to minor version
- * 28 on 112811 to add Texture::_auto_texture_scale.  Bumped to minor version
- * 29 on 121711 to add GeomVertexColumn::_column_alignment.  Bumped to minor
- * version 30 on 12212 to add Texture::_pad_*_size.  Bumped to minor version
- * 31 on 21612 to add DepthOffsetAttrib::_min_value, _max_value.  Bumped to
- * minor version 32 on 61112 to add Texture::_has_read_mipmaps.  Bumped to
- * minor version 33 on 81713 to add UvScrollNode::_w_speed.  Bumped to minor
- * version 34 on 91614 to add ScissorAttrib::_off.  Bumped to minor version 35
- * on 12314 to change StencilAttrib.  Bumped to minor version 36 on 12914 to
- * add samplers and lod settings.  Bumped to minor version 37 on 12215 to add
- * GeomVertexArrayFormat::_divisor.  Bumped to minor version 38 on 41515 to
- * add various Bullet classes.  Bumped to minor version 39 on 1916 to change
- * lights and materials.  Bumped to minor version 40 on 11116 to make
- * NodePaths writable.
- */
+static const unsigned short _bam_minor_ver = 41;
+// Bumped to minor version 14 on 2007-12-19 to change default ColorAttrib.
+// Bumped to minor version 15 on 2008-04-09 to add TextureAttrib::_implicit_sort.
+// Bumped to minor version 16 on 2008-05-13 to add Texture::_quality_level.
+// Bumped to minor version 17 on 2008-08-06 to add PartBundle::_anim_preload.
+// Bumped to minor version 18 on 2008-08-14 to add Texture::_simple_ram_image.
+// Bumped to minor version 19 on 2008-08-14 to add PandaNode::_bounds_type.
+// Bumped to minor version 20 on 2009-04-21 to add MovingPartBase::_forced_channel.
+// Bumped to minor version 21 on 2008-02-26 to add BamEnums::BamObjectCode.
+// Bumped to minor version 22 on 2009-07-31 to add UvScrollNode R speed.
+// Bumped to minor version 23 on 2010-05-04 to add internal TextureAttrib overrides.
+// Bumped to minor version 24 on 2010-05-04 to add internal TexMatrixAttrib overrides.
+// Bumped to minor version 25 on 2011-06-22 to add support for caching movie files.
+// Bumped to minor version 26 on 2011-08-05 to add multiview (stereo) Textures.
+// Bumped to minor version 27 on 2011-10-09 to add stdfloat_double.
+// Bumped to minor version 28 on 2011-11-28 to add Texture::_auto_texture_scale.
+// Bumped to minor version 29 on 2011-12-17 to add GeomVertexColumn::_column_alignment.
+// Bumped to minor version 30 on 2012-01-22 to add Texture::_pad_*_size.
+// Bumped to minor version 31 on 2012-02-16 to add DepthOffsetAttrib::_min_value, _max_value.
+// Bumped to minor version 32 on 2012-06-11 to add Texture::_has_read_mipmaps.
+// Bumped to minor version 33 on 2013-08-17 to add UvScrollNode::_w_speed.
+// Bumped to minor version 34 on 2014-09-16 to add ScissorAttrib::_off.
+// Bumped to minor version 35 on 2014-12-03 to change StencilAttrib.
+// Bumped to minor version 36 on 2014-12-09 to add samplers and lod settings.
+// Bumped to minor version 37 on 2015-01-22 to add GeomVertexArrayFormat::_divisor.
+// Bumped to minor version 38 on 2015-04-15 to add various Bullet classes.
+// Bumped to minor version 39 on 2016-01-09 to change lights and materials.
+// Bumped to minor version 40 on 2016-01-11 to make NodePaths writable.
+// Bumped to minor version 41 on 2016-03-02 to change LensNode, Lens, and Camera.
 
 
 #endif
 #endif

+ 2 - 1
panda/src/putil/bamReader.cxx

@@ -236,7 +236,8 @@ read_object() {
  * This flavor of read_object() returns both a TypedWritable and a
  * This flavor of read_object() returns both a TypedWritable and a
  * ReferenceCount pointer to the same object, so the reference count may be
  * ReferenceCount pointer to the same object, so the reference count may be
  * tracked reliably, without having to know precisely what type of object we
  * tracked reliably, without having to know precisely what type of object we
- * have.  It returns true on success, or false on failure.
+ * have.
+ * @return true on success, or false on failure.
  */
  */
 bool BamReader::
 bool BamReader::
 read_object(TypedWritable *&ptr, ReferenceCount *&ref_ptr) {
 read_object(TypedWritable *&ptr, ReferenceCount *&ref_ptr) {

+ 16 - 0
panda/src/putil/bamWriter.I

@@ -34,6 +34,22 @@ get_filename() const {
   return empty_filename;
   return empty_filename;
 }
 }
 
 
+/**
+ * Returns the major version number of the Bam file currently being written.
+ */
+INLINE int BamWriter::
+get_file_major_ver() const {
+  return _file_major;
+}
+
+/**
+ * Returns the minor version number of the Bam file currently being written.
+ */
+INLINE int BamWriter::
+get_file_minor_ver() const {
+  return _file_minor;
+}
+
 /**
 /**
  * Returns the endian preference indicated by the Bam file currently being
  * Returns the endian preference indicated by the Bam file currently being
  * written.  This does not imply that every number is stored using the
  * written.  This does not imply that every number is stored using the

+ 4 - 0
panda/src/putil/bamWriter.cxx

@@ -41,6 +41,8 @@ BamWriter(DatagramSink *target) :
   _next_pta_id = 1;
   _next_pta_id = 1;
   _long_pta_id = false;
   _long_pta_id = false;
 
 
+  _file_major = _bam_major_ver;
+  _file_minor = _bam_minor_ver;
   _file_endian = bam_endian;
   _file_endian = bam_endian;
   _file_stdfloat_double = bam_stdfloat_double;
   _file_stdfloat_double = bam_stdfloat_double;
   _file_texture_mode = bam_texture_mode;
   _file_texture_mode = bam_texture_mode;
@@ -96,6 +98,8 @@ init() {
   _next_pta_id = 1;
   _next_pta_id = 1;
   _long_pta_id = false;
   _long_pta_id = false;
 
 
+  _file_major = _bam_major_ver;
+  _file_minor = _bam_minor_ver;
   _file_endian = bam_endian;
   _file_endian = bam_endian;
   _file_texture_mode = bam_texture_mode;
   _file_texture_mode = bam_texture_mode;
 
 

+ 4 - 0
panda/src/putil/bamWriter.h

@@ -74,6 +74,9 @@ PUBLISHED:
   bool has_object(const TypedWritable *obj) const;
   bool has_object(const TypedWritable *obj) const;
   void flush();
   void flush();
 
 
+  INLINE int get_file_major_ver() const;
+  INLINE int get_file_minor_ver() const;
+
   INLINE BamEndian get_file_endian() const;
   INLINE BamEndian get_file_endian() const;
   INLINE bool get_file_stdfloat_double() const;
   INLINE bool get_file_stdfloat_double() const;
 
 
@@ -115,6 +118,7 @@ private:
   int enqueue_object(const TypedWritable *object);
   int enqueue_object(const TypedWritable *object);
   bool flush_queue();
   bool flush_queue();
 
 
+  int _file_major, _file_minor;
   BamEndian _file_endian;
   BamEndian _file_endian;
   bool _file_stdfloat_double;
   bool _file_stdfloat_double;
   BamTextureMode _file_texture_mode;
   BamTextureMode _file_texture_mode;

+ 7 - 0
panda/src/putil/config_util.cxx

@@ -143,6 +143,13 @@ ConfigVariableBool preload_simple_textures
           "in a sub-thread.  It's not generally necessary if you are "
           "in a sub-thread.  It's not generally necessary if you are "
           "loading bam files that were generated via egg2bam."));
           "loading bam files that were generated via egg2bam."));
 
 
+ConfigVariableBool compressed_textures
+("compressed-textures", false,
+ PRC_DESC("Set this to true to compress textures as they are loaded into "
+          "texture memory, if the driver supports this.  Specifically, this "
+          "changes the meaning of set_compression(Texture::CM_default) to "
+          "Texture::CM_on."));
+
 ConfigVariableBool cache_check_timestamps
 ConfigVariableBool cache_check_timestamps
 ("cache-check-timestamps", true,
 ("cache-check-timestamps", true,
  PRC_DESC("Set this true to check the timestamps on disk (when possible) "
  PRC_DESC("Set this true to check the timestamps on disk (when possible) "

+ 1 - 0
panda/src/putil/config_util.h

@@ -46,6 +46,7 @@ extern ConfigVariableDouble sleep_precision;
 
 
 extern EXPCL_PANDA_PUTIL ConfigVariableBool preload_textures;
 extern EXPCL_PANDA_PUTIL ConfigVariableBool preload_textures;
 extern EXPCL_PANDA_PUTIL ConfigVariableBool preload_simple_textures;
 extern EXPCL_PANDA_PUTIL ConfigVariableBool preload_simple_textures;
+extern EXPCL_PANDA_PUTIL ConfigVariableBool compressed_textures;
 extern EXPCL_PANDA_PUTIL ConfigVariableBool cache_check_timestamps;
 extern EXPCL_PANDA_PUTIL ConfigVariableBool cache_check_timestamps;
 
 
 extern EXPCL_PANDA_PUTIL void init_libputil();
 extern EXPCL_PANDA_PUTIL void init_libputil();

+ 8 - 0
panda/src/putil/loaderOptions.cxx

@@ -28,12 +28,16 @@ LoaderOptions(int flags) :
   // Shadowing the variables in config_util for static init ordering issues.
   // Shadowing the variables in config_util for static init ordering issues.
   static ConfigVariableBool *preload_textures;
   static ConfigVariableBool *preload_textures;
   static ConfigVariableBool *preload_simple_textures;
   static ConfigVariableBool *preload_simple_textures;
+  static ConfigVariableBool *compressed_textures;
   if (preload_textures == NULL) {
   if (preload_textures == NULL) {
     preload_textures = new ConfigVariableBool("preload-textures", true);
     preload_textures = new ConfigVariableBool("preload-textures", true);
   }
   }
   if (preload_simple_textures == NULL) {
   if (preload_simple_textures == NULL) {
     preload_simple_textures = new ConfigVariableBool("preload-simple-textures", false);
     preload_simple_textures = new ConfigVariableBool("preload-simple-textures", false);
   }
   }
+  if (compressed_textures == NULL) {
+    compressed_textures = new ConfigVariableBool("compressed-textures", false);
+  }
 
 
   if (*preload_textures) {
   if (*preload_textures) {
     _texture_flags |= TF_preload;
     _texture_flags |= TF_preload;
@@ -41,6 +45,9 @@ LoaderOptions(int flags) :
   if (*preload_simple_textures) {
   if (*preload_simple_textures) {
     _texture_flags |= TF_preload_simple;
     _texture_flags |= TF_preload_simple;
   }
   }
+  if (*compressed_textures) {
+    _texture_flags |= TF_allow_compression;
+  }
 }
 }
 
 
 /**
 /**
@@ -77,6 +84,7 @@ output(ostream &out) const {
   write_texture_flag(out, sep, "TF_preload_simple", TF_preload_simple);
   write_texture_flag(out, sep, "TF_preload_simple", TF_preload_simple);
   write_texture_flag(out, sep, "TF_allow_1d", TF_allow_1d);
   write_texture_flag(out, sep, "TF_allow_1d", TF_allow_1d);
   write_texture_flag(out, sep, "TF_generate_mipmaps", TF_generate_mipmaps);
   write_texture_flag(out, sep, "TF_generate_mipmaps", TF_generate_mipmaps);
+  write_texture_flag(out, sep, "TF_allow_compression", TF_allow_compression);
   if (sep.empty()) {
   if (sep.empty()) {
     out << "0";
     out << "0";
   }
   }

+ 1 - 0
panda/src/putil/loaderOptions.h

@@ -45,6 +45,7 @@ PUBLISHED:
     TF_multiview         = 0x0040,  // Load a multiview texture in pages
     TF_multiview         = 0x0040,  // Load a multiview texture in pages
     TF_integer           = 0x0080,  // Load as an integer (RGB) texture
     TF_integer           = 0x0080,  // Load as an integer (RGB) texture
     TF_float             = 0x0100,  // Load as a floating-point (depth) texture
     TF_float             = 0x0100,  // Load as a floating-point (depth) texture
+    TF_allow_compression = 0x0200,  // Consider compressing RAM image
   };
   };
 
 
   LoaderOptions(int flags = LF_search | LF_report_errors);
   LoaderOptions(int flags = LF_search | LF_report_errors);

+ 1 - 0
panda/src/text/dynamicTextFont.cxx

@@ -42,6 +42,7 @@
 // #include "renderModeAttrib.h" #include "antialiasAttrib.h"
 // #include "renderModeAttrib.h" #include "antialiasAttrib.h"
 #include "colorAttrib.h"
 #include "colorAttrib.h"
 #include "textureAttrib.h"
 #include "textureAttrib.h"
+#include "transparencyAttrib.h"
 
 
 TypeHandle DynamicTextFont::_type_handle;
 TypeHandle DynamicTextFont::_type_handle;
 
 

+ 15 - 0
panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

@@ -804,6 +804,21 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
     }
     }
     break;
     break;
 
 
+  case TransparencyAttrib::M_premultiplied_alpha:
+    {
+      // Implement a color mask, with pre-multiplied alpha blending.
+      int op_a = get_color_blend_op(ColorBlendAttrib::O_one);
+      int op_b = get_color_blend_op(ColorBlendAttrib::O_one_minus_incoming_alpha);
+
+      if (srgb_blend) {
+        _c->zb->store_pix_func = store_pixel_funcs_sRGB[op_a][op_b][color_channels];
+      } else {
+        _c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
+      }
+      color_write_state = 2;   // cgeneral
+    }
+    break;
+
   default:
   default:
     break;
     break;
   }
   }