瀏覽代碼

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

rdb 9 年之前
父節點
當前提交
6d28772ab3
共有 95 個文件被更改,包括 1066 次插入2014 次删除
  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
 load-file-type egg pandaegg
 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):

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

@@ -532,12 +532,19 @@ class ShowBase(DirectObject.DirectObject):
             del self.winList
             del self.pipe
 
-    def makeDefaultPipe(self, printPipeTypes = True):
+    def makeDefaultPipe(self, printPipeTypes = None):
         """
         Creates the default GraphicsPipe, which will be used to make
         windows unless otherwise specified.
         """
         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()
         if printPipeTypes:
             selection.printPipeTypes()
@@ -567,7 +574,6 @@ class ShowBase(DirectObject.DirectObject):
         Creates all GraphicsPipes that the system knows about and fill up
         self.pipeList with them.
         """
-        shouldPrintPipes = 0
         selection = GraphicsPipeSelection.getGlobalPtr()
         selection.loadAuxModules()
 

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

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

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

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

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

@@ -26,6 +26,7 @@
 #include "cppPreprocessor.h"
 #include "cppTemplateScope.h"
 #include "cppClassTemplateParameter.h"
+#include "cppConstType.h"
 #include "cppFunctionType.h"
 #include "cppUsing.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));
 
   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 {
     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
  * an undocumented inline version of PyErr_Occurred()).
  */
-bool Dtool_CheckErrorOccurred() {
+bool _Dtool_CheckErrorOccurred() {
   if (_PyErr_OCCURRED()) {
     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.
+EXPCL_INTERROGATEDB bool _Dtool_CheckErrorOccurred();
 
 #ifdef NDEBUG
 // _PyErr_OCCURRED is an undocumented inline version of PyErr_Occurred.
 #define Dtool_CheckErrorOccurred() (_PyErr_OCCURRED() != NULL)
 #else
-EXPCL_INTERROGATEDB bool Dtool_CheckErrorOccurred();
+#define Dtool_CheckErrorOccurred() _Dtool_CheckErrorOccurred()
 #endif
 
 EXPCL_INTERROGATEDB PyObject *Dtool_Raise_AssertionError();

+ 1 - 1
makepanda/config.in

@@ -41,7 +41,7 @@ framebuffer-software #f
 # consistent with the other framebuffer requirements.
 
 depth-bits 1
-color-bits 1
+color-bits 1 1 1
 alpha-bits 0
 stencil-bits 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')
   OPTS=['DIR:panda/metalibs/pandagles', 'BUILDING:PANDAGLES', 'GLES', 'EGL']
   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='p3glesgsg_config_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')
   OPTS=['DIR:panda/metalibs/pandagles2', 'BUILDING:PANDAGLES2', 'GLES2', 'EGL']
   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='p3gles2gsg_config_gles2gsg.obj')
   TargetAdd('libpandagles2.dll', input='p3gles2gsg_gles2gsg.obj')

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

@@ -16,7 +16,9 @@
 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(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 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);
 }
+
+/**
+ * @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 "pnmImage.h"
+#include "texture.h"
+#include "texturePeeker.h"
 
 /**
  *
@@ -29,6 +31,7 @@ class EXPCL_PANDABULLET BulletHeightfieldShape : public BulletShape {
 
 PUBLISHED:
   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 void operator = (const BulletHeightfieldShape &copy);
   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 {
 
-  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;
   LPoint3 get_position_world_on_a() 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_b() const;
 

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

@@ -138,6 +138,9 @@ get_default() {
     break;
   case 1:
     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;
   case 3:
     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
   // formats that are supported for render-to-texture.
 
-  static const int num_formats = 13;
+  static const int num_formats = 15;
   static const struct {
     unsigned char color_bits, red_bits, green_bits, blue_bits, alpha_bits;
     bool has_float;
     Texture::Format format;
   } 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,  1, false, Texture::F_rgba },
     { 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()) {
     display_cat.debug()
       << "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";
   }
 
@@ -3109,7 +3109,7 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
   FrameBufferProperties fbp;
   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;
   if (is_point) {
     flags |= GraphicsPipe::BF_size_square;
@@ -3124,13 +3124,13 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
   // error
   PT(Texture) tex = new Texture(light->get_name());
   if (is_point) {
-    if (light->_sb_xsize != light->_sb_ysize) {
+    if (light->_sb_size[0] != light->_sb_size[1]) {
       display_cat.error()
         << "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 {
-    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();
   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);
     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:
     dxgsg9_cat.error()
       << "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;
   } else if (cmp_nocase_uh(string, "dual") == 0) {
     return AM_dual;
+  } else if (cmp_nocase_uh(string, "premultiplied") == 0) {
+    return AM_premultiplied;
   } else {
     return AM_unspecified;
   }
@@ -260,6 +262,8 @@ ostream &operator << (ostream &out, EggRenderMode::AlphaMode mode) {
     return out << "binary";
   case EggRenderMode::AM_dual:
     return out << "dual";
+  case EggRenderMode::AM_premultiplied:
+    return out << "premultiplied";
   }
 
   nassertr(false, out);

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

@@ -45,7 +45,8 @@ PUBLISHED:
     AM_ms,      // TransparencyAttrib::M_multisample
     AM_ms_mask, // TransparencyAttrib::M_multisample_mask
     AM_binary,  // TransparencyAttrib::M_binary
-    AM_dual     // TransparencyAttrib::M_dual
+    AM_dual,    // TransparencyAttrib::M_dual
+    AM_premultiplied // TransparencyAttrib::M_premultiplied_alpha
   };
 
   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;
   switch (egg_tex->get_texture_type()) {
   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));
     break;
 
+  case EggRenderMode::AM_premultiplied:
+    add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_premultiplied_alpha));
+    break;
+
   default:
     break;
   }

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

@@ -686,12 +686,15 @@ convert_primitive(const GeomVertexData *vertex_data,
         break;
       case TransparencyAttrib::M_alpha:
         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 {
           tex_trans = EggRenderMode::AM_blend;
         }
         break;
+      case TransparencyAttrib::M_premultiplied_alpha:
+        tex_trans = EggRenderMode::AM_premultiplied;
+        break;
       case TransparencyAttrib::M_multisample:
         tex_trans = EggRenderMode::AM_ms;
         break;
@@ -705,7 +708,6 @@ convert_primitive(const GeomVertexData *vertex_data,
         tex_trans = EggRenderMode::AM_dual;
         break;
       default:  // intentional fall-through
-      case TransparencyAttrib::M_notused:
         break;
     }
     if (tex_trans != EggRenderMode::AM_unspecified) {

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

@@ -26,41 +26,6 @@ ConfigureFn(config_egldisplay) {
   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
  * 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);
 #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

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

@@ -10,49 +10,3 @@
  * @author rdb
  * @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;
 
-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(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);
   if (!eglInitialize(_egl_display, NULL, NULL)) {
     egldisplay_cat.error()
@@ -94,38 +38,6 @@ eglGraphicsPipe(const string &display) {
       << "Couldn't bind EGL to the OpenGL ES API: "
       << 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() {
-  release_hidden_cursor();
-  if (_im) {
-    XCloseIM(_im);
-  }
-  if (_display) {
-    XCloseDisplay(_display);
-  }
   if (_egl_display) {
     if (!eglTerminate(_egl_display)) {
       egldisplay_cat.error() << "Failed to terminate EGL display: "
@@ -168,26 +73,6 @@ pipe_constructor() {
   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.
  */
@@ -318,89 +203,3 @@ make_output(const string &name,
   // Nothing else left to try.
   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
 
 #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
   #include "gles2gsg.h"
@@ -46,7 +42,7 @@ class eglGraphicsWindow;
  * This graphics pipe represents the interface for creating OpenGL ES graphics
  * windows on an X-based (e.g.  Unix) client.
  */
-class eglGraphicsPipe : public GraphicsPipe {
+class eglGraphicsPipe : public x11GraphicsPipe {
 public:
   eglGraphicsPipe(const string &display = string());
   virtual ~eglGraphicsPipe();
@@ -54,29 +50,6 @@ public:
   virtual string get_interface_name() const;
   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:
   virtual PT(GraphicsOutput) make_output(const string &name,
                                          const FrameBufferProperties &fb_prop,
@@ -89,40 +62,16 @@ protected:
                                          bool &precertify);
 
 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;
 
-  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:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
   static void init_type() {
-    GraphicsPipe::init_type();
+    x11GraphicsPipe::init_type();
     register_type(_type_handle, "eglGraphicsPipe",
-                  GraphicsPipe::get_class_type());
+                  x11GraphicsPipe::get_class_type());
   }
   virtual TypeHandle get_type() const {
     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,
   // 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.
     _fbprops.set_force_software(1);
     _fbprops.set_force_hardware(0);

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

@@ -10,11 +10,3 @@
  * @author rdb
  * @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 "get_x11.h"
 
-#include <errno.h>
-#include <sys/time.h>
-
-#ifdef HAVE_LINUX_INPUT_H
-#include <linux/input.h>
-#endif
-
 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,
                   GraphicsStateGuardian *gsg,
                   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;
   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_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();
 }
 
-/**
- * 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.
  */
@@ -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?
     egldisplay_cat.error()
       << "No X visual: cannot open window.\n";
     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;
   }
-  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);
   if (eglGetError() != EGL_SUCCESS) {
@@ -721,923 +294,5 @@ open_window() {
   }
   _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;
 }
-
-/**
- * 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 "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.
  */
-class eglGraphicsWindow : public GraphicsWindow {
+class eglGraphicsWindow : public x11GraphicsWindow {
 public:
   eglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
                     const string &name,
@@ -40,70 +38,22 @@ public:
   virtual void end_frame(FrameMode mode, Thread *current_thread);
   virtual void end_flip();
 
-  virtual void process_events();
-  virtual void set_properties_now(WindowProperties &properties);
-
-  INLINE X11_Window get_xwindow() const;
-
 protected:
   virtual void close_window();
   virtual bool open_window();
 
 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;
   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:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
   static void init_type() {
-    GraphicsWindow::init_type();
+    x11GraphicsWindow::init_type();
     register_type(_type_handle, "eglGraphicsWindow",
-                  GraphicsWindow::get_class_type());
+                  x11GraphicsWindow::get_class_type());
   }
   virtual TypeHandle get_type() const {
     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) {
     // Reset to an empty 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);
@@ -275,7 +281,9 @@ open_read_write_file(const Filename &file, bool truncate) {
   if (truncate) {
     // Reset to an empty 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);

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

@@ -31,6 +31,8 @@ ConfigVariableBool show_frame_rate_meter
 ("show-frame-rate-meter", false);
 ConfigVariableBool show_scene_graph_analyzer_meter
 ("show-scene-graph-analyzer-meter", false);
+ConfigVariableBool print_pipe_types
+("print-pipe-types", true);
 ConfigVariableString window_type
 ("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 ConfigVariableBool show_frame_rate_meter;
 extern ConfigVariableBool show_scene_graph_analyzer_meter;
+extern ConfigVariableBool print_pipe_types;
 extern ConfigVariableString window_type;
 
 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
   // aux-display Configrc variables.
   GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
-  selection->print_pipe_types();
+
+  if (print_pipe_types) {
+    selection->print_pipe_types();
+  }
+
   _default_pipe = selection->make_default_pipe();
 
   if (_default_pipe == (GraphicsPipe*)NULL) {

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

@@ -509,6 +509,7 @@ rebuild_bitplanes() {
 
   // Now create the FBO's.
   _have_any_color = false;
+  bool have_any_depth = false;
 
   if (num_fbos > _fbo.size()) {
     // Generate more FBO handles.
@@ -542,8 +543,10 @@ rebuild_bitplanes() {
     // For all slots, update the slot.
     if (_use_depth_stencil) {
       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) {
       bind_slot(layer, rb_resize, attach, RTP_depth, GL_DEPTH_ATTACHMENT_EXT);
+      have_any_depth = true;
     }
 
     int next = GL_COLOR_ATTACHMENT0_EXT;
@@ -581,11 +584,23 @@ rebuild_bitplanes() {
     }
 #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);
   } else if (_fb_properties.get_depth_bits() > 16) {
     _fb_properties.set_depth_bits(24);
-  } else {
+  } else if (_fb_properties.get_depth_bits() > 0) {
     _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

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

@@ -169,7 +169,7 @@ static const string default_fshader =
   "#version 130\n"
   "in vec2 texcoord;\n"
   "in vec4 color;\n"
-  "out vec4 p3d_FragColor;"
+  "out vec4 p3d_FragColor;\n"
   "uniform sampler2D p3d_Texture0;\n"
   "uniform vec4 p3d_TexAlphaOnly;\n"
 #else
@@ -414,7 +414,12 @@ debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei l
     break;
 
   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;
 
   case GL_DEBUG_SEVERITY_LOW:
@@ -1947,6 +1952,16 @@ reset() {
   }
 #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;
   if (has_extension("GL_EXT_framebuffer_multisample")) {
     _supports_framebuffer_multisample = true;
@@ -2527,7 +2542,7 @@ reset() {
 
   if (core_profile) {
     // TODO: better detection mechanism?
-    _supports_stencil = true;
+    _supports_stencil = support_stencil;
   }
 #ifdef SUPPORT_FIXED_FUNCTION
   else if (support_stencil) {
@@ -2998,7 +3013,7 @@ clear(DrawableRegion *clearable) {
     mask |= GL_DEPTH_BUFFER_BIT;
   }
 
-  if (clearable->get_clear_stencil_active()) {
+  if (_supports_stencil && clearable->get_clear_stencil_active()) {
     glStencilMask(~0);
     glClearStencil(clearable->get_clear_stencil());
     mask |= GL_STENCIL_BUFFER_BIT;
@@ -4826,6 +4841,7 @@ update_texture(TextureContext *tc, bool force) {
     if (gtc->was_properties_modified()) {
       specify_texture(gtc, tex->get_default_sampler());
     }
+
     bool okflag = upload_texture(gtc, force, tex->uses_mipmaps());
     if (!okflag) {
       GLCAT.error()
@@ -6247,7 +6263,9 @@ do_issue_render_mode() {
   }
   report_my_gl_errors();
 
+#ifdef SUPPORT_FIXED_FUNCTION
   do_point_size();
+#endif
 }
 
 /**
@@ -6728,6 +6746,19 @@ do_issue_blending() {
     }
     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:
     // We need to enable *both* of these in M_multisample case.
     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
  * changed that might affect this.
  */
+#ifdef SUPPORT_FIXED_FUNCTION
 void CLP(GraphicsStateGuardian)::
 do_point_size() {
-#ifndef OPENGLES_2
   if (!_point_perspective) {
     // Normal, constant-sized points.  Here _point_size is a width in pixels.
     static LVecBase3f constant(1.0f, 0.0f, 0.0f);
@@ -12753,8 +12784,8 @@ do_point_size() {
   }
 
   report_my_gl_errors();
-#endif
 }
+#endif
 
 /**
  * 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::CompressionMode compression, int n);
 
+#ifdef SUPPORT_FIXED_FUNCTION
   void do_point_size();
+#endif
 
   enum AutoAntialiasMode {
     AA_poly,
@@ -828,6 +830,11 @@ public:
   PFNGLGENERATETEXTUREMIPMAPPROC _glGenerateTextureMipmap;
 #endif
 
+#ifndef OPENGLES
+  bool _supports_empty_framebuffer;
+  PFNGLFRAMEBUFFERPARAMETERIPROC _glFramebufferParameteri;
+#endif
+
   bool _supports_framebuffer_multisample;
   bool _supports_framebuffer_multisample_coverage_nv;
   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;
 
     default:
-      GLCAT.warning() << "Ignoring uniform '" << name_buffer
+      GLCAT.info() << "Ignoring uniform '" << name_buffer
         << "' with unsupported type 0x" << hex << param_type << dec << "\n";
       continue;
     }
@@ -1462,24 +1462,27 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
         return;
       }
 #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
         // and then forget about it.
         _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 "
           "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
 ("driver-compress-textures", false,
  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 compressed_textures;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool driver_compress_textures;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool driver_generate_mipmaps;
 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
- * newly-created lens.  This is read from the Configrc file.
+ * newly-created lens.  This is read from the Config.prc file.
  */
 PN_stdfloat Lens::
 get_default_near() {
@@ -148,7 +148,7 @@ get_default_near() {
 
 /**
  * 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::
 get_default_far() {
@@ -1930,6 +1930,35 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
   dg.add_stdfloat(_near_distance);
   dg.add_stdfloat(_far_distance);
   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();
   _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;
 }
 

+ 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);
 }
 
+/**
+ * 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
  * 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;
 }
+
+/**
+ * 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:
   static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
 
 public:
   virtual TypeHandle get_type() const {

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

@@ -1678,9 +1678,12 @@ clear_ram_mipmap_images() {
  */
 INLINE void Texture::
 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();
-  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.
  */
 INLINE void Texture::
-set_loaded_from_image() {
+set_loaded_from_image(bool flag) {
   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.
  */
 INLINE void Texture::
-set_loaded_from_txo() {
+set_loaded_from_txo(bool flag) {
   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
       // etc.
       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;
 
   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,
           cdata->_primary_file_num_channels, cdata->_alpha_file_channel,
           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 &&
       cdata->_ram_images.size() == 1) {
-    do_generate_ram_mipmap_images(cdata);
+    do_generate_ram_mipmap_images(cdata, false);
     modified = true;
   }
 
@@ -4725,7 +4731,7 @@ do_compress_ram_image(CData *cdata, Texture::CompressionMode compression,
     if (!do_has_all_ram_mipmap_images(cdata)) {
       // If we're about to compress the RAM image, we should ensure that we
       // have all of the mipmap levels first.
-      do_generate_ram_mipmap_images(cdata);
+      do_generate_ram_mipmap_images(cdata, false);
     }
 
     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::
 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;
   while (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::
-do_generate_ram_mipmap_images(CData *cdata) {
+do_generate_ram_mipmap_images(CData *cdata, bool allow_recompress) {
   nassertv(do_has_ram_image(cdata));
 
   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
     // compression mode.  We don't need to bother compressing the first image
     // (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);
     // Now restore the toplevel image.
     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);
     } else {
       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 we're about to compress the RAM image, we should ensure that we have
     // all of the mipmap levels first.
-    do_generate_ram_mipmap_images(cdata);
+    do_generate_ram_mipmap_images(cdata, false);
   }
 
   RamImages compressed_ram_images;

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

@@ -300,70 +300,115 @@ PUBLISHED:
 
   INLINE bool has_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 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 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 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 void set_x_size(int x_size);
+  MAKE_PROPERTY(x_size, get_x_size, set_x_size);
+
   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 void set_z_size(int z_size);
+  MAKE_PROPERTY(z_size, get_z_size, set_z_size);
+
   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_components() const;
   INLINE int get_component_width() 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;
-  MAKE_PROPERTY(num_views, get_num_views);
+
   MAKE_PROPERTY(num_pages, get_num_pages);
   MAKE_PROPERTY(num_components, get_num_components);
   MAKE_PROPERTY(component_width, get_component_width);
   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);
 
-  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 void set_wrap_u(WrapMode wrap);
+  MAKE_PROPERTY(wrap_u, get_wrap_u, set_wrap_u);
+
   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 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_magfilter() 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 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_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 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 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 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(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_effective_quality_level() const;
+  INLINE void set_quality_level(QualityLevel quality_level);
   MAKE_PROPERTY(quality_level, get_quality_level, set_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_z_size(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_uncompressed_ram_image() const;
@@ -381,6 +427,12 @@ PUBLISHED:
   INLINE size_t get_ram_page_size() const;
   INLINE size_t get_expected_ram_image_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 CompressionMode get_ram_image_compression() const;
   INLINE CPTA_uchar get_uncompressed_ram_image();
@@ -395,6 +447,10 @@ PUBLISHED:
   virtual bool get_keep_ram_image() 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,
                                  QualityLevel quality_level = QL_default,
                                  GraphicsStateGuardianBase *gsg = NULL);
@@ -421,6 +477,9 @@ PUBLISHED:
   INLINE void clear_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_y_size() const;
   INLINE bool has_simple_ram_image() const;
@@ -432,6 +491,10 @@ PUBLISHED:
   void generate_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();
 
   INLINE UpdateSeq get_properties_modified() const;
@@ -441,9 +504,9 @@ PUBLISHED:
   MAKE_PROPERTY(image_modified, get_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 AutoTextureScale get_auto_texture_scale() const;
+  INLINE void set_auto_texture_scale(AutoTextureScale scale);
   MAKE_PROPERTY(auto_texture_scale, get_auto_texture_scale,
                                     set_auto_texture_scale);
 
@@ -470,22 +533,6 @@ PUBLISHED:
   INLINE static bool has_textures_power_2();
 
 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_y_size() const;
@@ -499,21 +546,29 @@ PUBLISHED:
   INLINE int get_orig_file_y_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);
 
-  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;
+  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;
+  MAKE_PROPERTY(loaded_from_txo, get_loaded_from_txo, set_loaded_from_txo);
 
   INLINE bool get_match_framebuffer_format() const;
   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 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,
                               PreparedGraphicsObjects *prepared_objects,
@@ -697,7 +752,7 @@ protected:
   INLINE void do_clear_ram_image(CData *cdata);
   void do_clear_simple_ram_image(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);
   virtual bool do_can_reload(const CData *cdata) const;
   bool do_reload(CData *cdata);

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

@@ -48,3 +48,11 @@ INLINE int TexturePeeker::
 get_z_size() const {
   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) {
   case Texture::F_depth_stencil:
   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_r16:
     _get_texel = get_texel_r;
     break;
 
@@ -109,23 +112,30 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) {
     break;
 
   case Texture::F_luminance:
+  case Texture::F_sluminance:
     _get_texel = get_texel_l;
     break;
 
   case Texture::F_luminance_alpha:
+  case Texture::F_sluminance_alpha:
   case Texture::F_luminance_alphamask:
     _get_texel = get_texel_la;
     break;
 
   case Texture::F_rgb:
+  case Texture::F_srgb:
   case Texture::F_rgb5:
   case Texture::F_rgb8:
   case Texture::F_rgb12:
+  case Texture::F_rgb16:
   case Texture::F_rgb332:
+  case Texture::F_r11_g11_b10:
+  case Texture::F_rgb9_e5:
     _get_texel = get_texel_rgb;
     break;
 
   case Texture::F_rgba:
+  case Texture::F_srgb_alpha:
   case Texture::F_rgbm:
   case Texture::F_rgba4:
   case Texture::F_rgba5:
@@ -133,10 +143,13 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) {
   case Texture::F_rgba12:
   case Texture::F_rgba16:
   case Texture::F_rgba32:
+  case Texture::F_rgb10_a2:
     _get_texel = get_texel_rgba;
     break;
   default:
     // Not supported.
+    gobj_cat.error() << "Unsupported texture peeker format: "
+      << Texture::format_format(_format) << endl;
     _image.clear();
     return;
   }
@@ -155,13 +168,70 @@ void TexturePeeker::
 lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const {
   int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_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);
   const unsigned char *p = _image.p() + (y * _x_size + x) * _pixel_width;
-
   (*_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).
  *

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

@@ -36,8 +36,11 @@ PUBLISHED:
   INLINE int get_y_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, 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,
                    PN_stdfloat min_u, PN_stdfloat min_v,
                    PN_stdfloat max_u, PN_stdfloat max_v) const;

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

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

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

@@ -26,9 +26,9 @@ Camera(const string &name, Lens *lens) :
   LensNode(name, lens),
   _active(true),
   _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_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();
   _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:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist,
+                                BamReader *manager);
 
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);

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

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

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

@@ -218,9 +218,18 @@ void LensNode::
 write_datagram(BamWriter *manager, Datagram &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::
 complete_pointers(TypedWritable **p_list, BamReader *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;
 }
 
@@ -259,5 +277,17 @@ void LensNode::
 fillin(DatagramIterator &scan, BamReader *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) {
   TypedWritable::fillin(scan, manager);
 
+  remove_all_children();
+
   string name = scan.get_string();
   set_name(name);
 

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

@@ -1853,8 +1853,8 @@ determine_bin_index() {
   string bin_name;
   _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();
     _draw_order = bin->get_draw_order();
   }
@@ -1864,10 +1864,11 @@ determine_bin_index() {
     // opaque or transparent, based on the transparency setting.
     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()) {
       case TransparencyAttrib::M_alpha:
+      case TransparencyAttrib::M_premultiplied_alpha:
       case TransparencyAttrib::M_dual:
         // These transparency modes require special back-to-front sorting.
         bin_name = "transparent";

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

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

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

@@ -36,7 +36,7 @@ PUBLISHED:
     // corresponded to M_none or M_alpha).
     M_none = 0,         // No transparency.
     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_mask, // Uses ms buffer, alpha values not modified.
     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::
 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();
   }
   _shadow_caster = caster;
-  _sb_xsize = buffer_xsize;
-  _sb_ysize = buffer_ysize;
+  _sb_size.set(buffer_xsize, buffer_ysize);
 
   if (buffer_sort != _sb_sort) {
     ShadowBuffers::iterator it;
@@ -59,6 +58,25 @@ set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_so
   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
  * 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);
   _shadow_caster = false;
-  _sb_xsize = 512;
-  _sb_ysize = 512;
+  _sb_size.set(512, 512);
   _sb_sort = -10;
   // set_initial_state(RenderState::make(ShaderAttrib::make_off(), 1000));
   // Backface culling helps eliminating artifacts.
@@ -57,8 +56,7 @@ LightLensNode(const LightLensNode &copy) :
   Light(copy),
   Camera(copy),
   _shadow_caster(copy._shadow_caster),
-  _sb_xsize(copy._sb_xsize),
-  _sb_ysize(copy._sb_ysize),
+  _sb_size(copy._sb_size),
   _sb_sort(-10)
 {
 }
@@ -126,8 +124,8 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   Light::write_datagram(manager, dg);
 
   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);
 }
 

+ 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, 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);
 
+PUBLISHED:
+  MAKE_PROPERTY(shadow_caster, is_shadow_caster);
+  MAKE_PROPERTY(shadow_buffer_size, get_shadow_buffer_size, set_shadow_buffer_size);
+
 protected:
   LightLensNode(const LightLensNode &copy);
   void clear_shadow_buffers();
 
+  LVecBase2i _sb_size;
   bool _shadow_caster;
-  int _sb_xsize, _sb_ysize, _sb_sort;
+  int _sb_sort;
 
   // This is really a map of GSG -> GraphicsOutput.
   typedef pmap<PT(GraphicsStateGuardianBase), PT(GraphicsOutputBase) > ShadowBuffers;

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

@@ -18,6 +18,7 @@ INLINE PointLight::CData::
 CData() :
   _specular_color(1.0f, 1.0f, 1.0f, 1.0f),
   _attenuation(1.0f, 0.0f, 0.0f),
+  _max_distance(make_inf((PN_stdfloat)0)),
   _point(0.0f, 0.0f, 0.0f)
 {
 }
@@ -29,6 +30,7 @@ INLINE PointLight::CData::
 CData(const PointLight::CData &copy) :
   _specular_color(copy._specular_color),
   _attenuation(copy._attenuation),
+  _max_distance(copy._max_distance),
   _point(copy._point)
 {
 }
@@ -88,9 +90,29 @@ set_attenuation(const LVecBase3 &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
- * 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::
 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::
 set_point(const LPoint3 &point) {

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

@@ -33,9 +33,12 @@ make_copy() const {
  * Bam file.
  */
 void PointLight::CData::
-write_datagram(BamWriter *, Datagram &dg) const {
+write_datagram(BamWriter *manager, Datagram &dg) const {
   _specular_color.write_datagram(dg);
   _attenuation.write_datagram(dg);
+  if (manager->get_file_minor_ver() >= 41) {
+    dg.add_stdfloat(_max_distance);
+  }
   _point.write_datagram(dg);
 }
 
@@ -44,9 +47,12 @@ write_datagram(BamWriter *, Datagram &dg) const {
  * relevant data from the BamFile for the new Light.
  */
 void PointLight::CData::
-fillin(DatagramIterator &scan, BamReader *) {
+fillin(DatagramIterator &scan, BamReader *manager) {
   _specular_color.read_datagram(scan);
   _attenuation.read_datagram(scan);
+  if (manager->get_file_minor_ver() >= 41) {
+    _max_distance = scan.get_stdfloat();
+  }
   _point.read_datagram(scan);
 }
 
@@ -127,6 +133,11 @@ write(ostream &out, int indent_level) const {
   }
   indent(out, indent_level + 2)
     << "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);
   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 void set_point(const LPoint3 &point);
   MAKE_PROPERTY(point, get_point, set_point);
@@ -75,6 +79,7 @@ private:
 
     LColor _specular_color;
     LVecBase3 _attenuation;
+    PN_stdfloat _max_distance;
     LPoint3 _point;
   };
 

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

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

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

@@ -18,7 +18,8 @@ INLINE Spotlight::CData::
 CData() :
   _exponent(50.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) :
   _exponent(copy._exponent),
   _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);
   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.
  */
 void Spotlight::CData::
-write_datagram(BamWriter *, Datagram &dg) const {
+write_datagram(BamWriter *manager, Datagram &dg) const {
   dg.add_stdfloat(_exponent);
   _specular_color.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.
  */
 void Spotlight::CData::
-fillin(DatagramIterator &scan, BamReader *) {
+fillin(DatagramIterator &scan, BamReader *manager) {
   _exponent = scan.get_stdfloat();
   _specular_color.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)
     << "exponent " << get_exponent() << "\n";
 
+  if (!cinf(get_max_distance())) {
+    indent(out, indent_level + 2)
+      << "max distance " << get_max_distance() << "\n";
+  }
+
   Lens *lens = get_lens();
   if (lens != (Lens *)NULL) {
     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);
   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;
 
   static PT(Texture) make_spot(int pixel_width, PN_stdfloat full_radius,
@@ -92,6 +96,7 @@ private:
     PN_stdfloat _exponent;
     LColor _specular_color;
     LVecBase3 _attenuation;
+    PN_stdfloat _max_distance;
   };
 
   PipelineCycler<CData> _cycler;

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

@@ -281,11 +281,11 @@ get_solver_iterations() const {
   return _desc.solverIterations;
 }
 
-/*
 /**
  * Used by PhysScene to query the sizes of arrays to allocate for the user
  * buffers in PhysxClothNode.
  */
+/*
 void PhysxClothDesc::
 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
 
 
-
-
-
-
-
-
-/*
 /**
  * Attaches a cloth vertex to a position in world space.
  */
+/*
 void PhysxSoftBody::
 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));
 }
+*/
 
 /**
  * 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
  * of a general triangle mesh is not clearly defined.
  */
+/*
 void PhysxSoftBody::
 attach_to_shape(PhysxShape *shape) {
 
@@ -570,6 +566,7 @@ attach_to_shape(PhysxShape *shape) {
   NxU32 attachmentFlags = 0; // --TODO--
   _ptr->attachToShape(shape->ptr(), attachmentFlags);
 }
+*/
 
 /**
  * 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
  * of a general triangle mesh is not clearly defined.
  */
+/*
 void PhysxSoftBody::
 attach_to_colliding_shapes() {
 
@@ -585,6 +583,7 @@ attach_to_colliding_shapes() {
   NxU32 attachmentFlags = 0; // --TODO--
   _ptr->attachToCollidingShapes(attachmentFlags);
 }
+*/
 
 /**
  * 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
  * effect.
  */
+/*
 void PhysxSoftBody::
 detach_from_shape(PhysxShape *shape) {
 
@@ -600,20 +600,24 @@ detach_from_shape(PhysxShape *shape) {
 
   _ptr->detachFromShape(shape->ptr());
 }
+*/
 
 /**
  * Frees a previously attached cloth point.
  */
+/*
 void PhysxSoftBody::
 free_vertex(unsigned int vertexId) {
 
   nassertv(_error_type == ET_ok);
   _ptr->freeVertex(vertexId);
 }
+*/
 
 /**
  * Attaches a cloth vertex to a local position within a shape.
  */
+/*
 void PhysxSoftBody::
 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),
                             attachmentFlags);
 }
+*/
 
 /**
  * Return the attachment status of the given vertex.
  */
+/*
 PhysxEnums::PhysxVertexAttachmentStatus PhysxSoftBody::
 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);
 }
+*/
 
 /**
  * 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
  * returned.
  */
+/*
 PhysxShape *PhysxSoftBody::
 get_vertex_attachment_shape(unsigned int vertexId) const {
 
@@ -655,12 +663,14 @@ get_vertex_attachment_shape(unsigned int vertexId) const {
 
   return shape;
 }
+*/
 
 /**
  * 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
  * the vertex is not attached, the return value is undefined.
  */
+/*
 LPoint3f PhysxSoftBody::
 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));
 }
+*/
 
 /**
  * Sets an external acceleration which affects all non attached particles of
  * the cloth.
  */
+/*
 void PhysxSoftBody::
 set_external_acceleration(LVector3f const &acceleration) {
 
@@ -683,10 +695,12 @@ set_external_acceleration(LVector3f const &acceleration) {
 
   _ptr->setExternalAcceleration(PhysxManager::vec3_to_nxVec3(acceleration));
 }
+*/
 
 /**
  * Sets an acceleration acting normal to the cloth surface at each vertex.
  */
+/*
 void PhysxSoftBody::
 set_wind_acceleration(LVector3f const &acceleration) {
 
@@ -695,33 +709,39 @@ set_wind_acceleration(LVector3f const &acceleration) {
 
   _ptr->setWindAcceleration(PhysxManager::vec3_to_nxVec3(acceleration));
 }
+*/
 
 /**
  * Retrieves the external acceleration which affects all non attached
  * particles of the cloth.
  */
+/*
 LVector3f PhysxSoftBody::
 get_external_acceleration() const {
 
   nassertr(_error_type == ET_ok, LVector3f::zero());
   return PhysxManager::nxVec3_to_vec3(_ptr->getExternalAcceleration());
 }
+*/
 
 /**
  * Retrieves the acceleration acting normal to the cloth surface at each
  * vertex
  */
+/*
 LVector3f PhysxSoftBody::
 get_wind_acceleration() const {
 
   nassertr(_error_type == ET_ok, LVector3f::zero());
   return PhysxManager::nxVec3_to_vec3(_ptr->getWindAcceleration());
 }
+*/
 
 /**
  * Applies a force (or impulse) defined in the global coordinate frame, to a
  * particular vertex of the cloth.
  */
+/*
 void PhysxSoftBody::
 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,
                          (NxForceMode) mode);
 }
+*/
 
 /**
  * Applies a radial force (or impulse) at a particular position.  All vertices
  * within radius will be affected with a quadratic drop-off.
  */
+/*
 void PhysxSoftBody::
 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,
                       (NxForceMode) mode);
 }
+*/
 
 /**
  * Applies a directed force (or impulse) at a particular position.  All
  * vertices within radius will be affected with a quadratic drop-off.
  */
+/*
 void PhysxSoftBody::
 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;
 }
 
-/*
 /**
  * Used by PhysScene to query the sizes of arrays to allocate for the user
  * buffers in PhysxSoftBodyNode.
  */
+/*
 void PhysxSoftBodyDesc::
 get_mesh_numbers(NxU32 &numVertices, NxU32 &numTriangles) {
 

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

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

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

@@ -34,10 +34,11 @@ PUBLISHED:
   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(ostream &out, int indent_level=0) const;

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

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

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

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

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

@@ -32,17 +32,16 @@ PUBLISHED:
   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(ostream &out, int indent_level=0) const;
 
-public:
-
 private:
   PT(PhysxWheelShape) _wheelShape;
   NodePath _np;

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

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

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

@@ -24,22 +24,23 @@ Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL;
  */
 Pipeline::
 Pipeline(const string &name, int num_stages) :
-  Namable(name)
+  Namable(name),
 #ifdef THREADED_PIPELINE
-  , _lock("Pipeline")
+  _num_stages(num_stages),
+  _lock("Pipeline")
+#else
+  _num_stages(1)
 #endif
 {
 #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.
   _clean.make_head();
@@ -53,9 +54,15 @@ Pipeline(const string &name, int num_stages) :
   // This flag is true only during the call to cycle().
   _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
 
-  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) {
   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
  * 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 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_alpha(float from_gamma, float to_gamma);
   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)
  *
- * 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::
 indirect_1d_lookup(const PNMImage &index_image, int channel,

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

@@ -16,6 +16,8 @@
 #include "filename.h"
 #include "pnmImage.h"
 
+#include FT_OUTLINE_H
+
 /**
  * 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
@@ -193,7 +195,6 @@ make_glyph(int glyph_index) {
 
       PN_stdfloat tex_x_size, tex_y_size, tex_x_orig, tex_y_orig;
       FT_BBox bounds;
-      TransparencyAttrib::Mode alpha_mode;
 
       // Calculate suitable texture dimensions for the signed distance field.
       // 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 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_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

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

@@ -236,7 +236,8 @@ read_object() {
  * This flavor of read_object() returns both a TypedWritable and a
  * ReferenceCount pointer to the same object, so the reference count may be
  * 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::
 read_object(TypedWritable *&ptr, ReferenceCount *&ref_ptr) {

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

@@ -34,6 +34,22 @@ get_filename() const {
   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
  * 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;
   _long_pta_id = false;
 
+  _file_major = _bam_major_ver;
+  _file_minor = _bam_minor_ver;
   _file_endian = bam_endian;
   _file_stdfloat_double = bam_stdfloat_double;
   _file_texture_mode = bam_texture_mode;
@@ -96,6 +98,8 @@ init() {
   _next_pta_id = 1;
   _long_pta_id = false;
 
+  _file_major = _bam_major_ver;
+  _file_minor = _bam_minor_ver;
   _file_endian = bam_endian;
   _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;
   void flush();
 
+  INLINE int get_file_major_ver() const;
+  INLINE int get_file_minor_ver() const;
+
   INLINE BamEndian get_file_endian() const;
   INLINE bool get_file_stdfloat_double() const;
 
@@ -115,6 +118,7 @@ private:
   int enqueue_object(const TypedWritable *object);
   bool flush_queue();
 
+  int _file_major, _file_minor;
   BamEndian _file_endian;
   bool _file_stdfloat_double;
   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 "
           "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
 ("cache-check-timestamps", true,
  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_simple_textures;
+extern EXPCL_PANDA_PUTIL ConfigVariableBool compressed_textures;
 extern EXPCL_PANDA_PUTIL ConfigVariableBool cache_check_timestamps;
 
 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.
   static ConfigVariableBool *preload_textures;
   static ConfigVariableBool *preload_simple_textures;
+  static ConfigVariableBool *compressed_textures;
   if (preload_textures == NULL) {
     preload_textures = new ConfigVariableBool("preload-textures", true);
   }
   if (preload_simple_textures == NULL) {
     preload_simple_textures = new ConfigVariableBool("preload-simple-textures", false);
   }
+  if (compressed_textures == NULL) {
+    compressed_textures = new ConfigVariableBool("compressed-textures", false);
+  }
 
   if (*preload_textures) {
     _texture_flags |= TF_preload;
@@ -41,6 +45,9 @@ LoaderOptions(int flags) :
   if (*preload_simple_textures) {
     _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_allow_1d", TF_allow_1d);
   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()) {
     out << "0";
   }

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

@@ -45,6 +45,7 @@ PUBLISHED:
     TF_multiview         = 0x0040,  // Load a multiview texture in pages
     TF_integer           = 0x0080,  // Load as an integer (RGB) 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);

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

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

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

@@ -804,6 +804,21 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
     }
     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:
     break;
   }