Explorar o código

Merge branch 'master' into vulkan

rdb %!s(int64=9) %!d(string=hai) anos
pai
achega
dc3799db25
Modificáronse 57 ficheiros con 691 adicións e 130 borrados
  1. 8 2
      direct/src/showbase/ShowBase.py
  2. 1 3
      dtool/src/interrogate/interrogateBuilder.cxx
  3. 1 1
      dtool/src/interrogatedb/py_panda.cxx
  4. 2 1
      dtool/src/interrogatedb/py_panda.h
  5. 7 0
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  6. 4 0
      panda/src/egg/eggRenderMode.cxx
  7. 2 1
      panda/src/egg/eggRenderMode.h
  8. 5 0
      panda/src/egg2pg/eggLoader.cxx
  9. 4 0
      panda/src/egg2pg/eggRenderState.cxx
  10. 5 3
      panda/src/egg2pg/eggSaver.cxx
  11. 10 2
      panda/src/express/virtualFileMountRamdisk.cxx
  12. 2 0
      panda/src/framework/config_framework.cxx
  13. 1 0
      panda/src/framework/config_framework.h
  14. 5 1
      panda/src/framework/pandaFramework.cxx
  15. 27 6
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  16. 2 0
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  17. 21 18
      panda/src/glstuff/glShaderContext_src.cxx
  18. 0 7
      panda/src/gobj/config_gobj.cxx
  19. 0 1
      panda/src/gobj/config_gobj.h
  20. 58 2
      panda/src/gobj/lens.cxx
  21. 38 0
      panda/src/gobj/matrixLens.cxx
  22. 2 0
      panda/src/gobj/matrixLens.h
  23. 5 2
      panda/src/gobj/texture.I
  24. 25 9
      panda/src/gobj/texture.cxx
  25. 1 1
      panda/src/gobj/texture.h
  26. 8 0
      panda/src/gobj/texturePeeker.I
  27. 72 2
      panda/src/gobj/texturePeeker.cxx
  28. 3 0
      panda/src/gobj/texturePeeker.h
  29. 24 2
      panda/src/pgraph/camera.cxx
  30. 2 0
      panda/src/pgraph/camera.h
  31. 1 0
      panda/src/pgraph/cullResult.cxx
  32. 1 1
      panda/src/pgraph/cullTraverserData.cxx
  33. 35 5
      panda/src/pgraph/lensNode.cxx
  34. 2 0
      panda/src/pgraph/pandaNode.cxx
  35. 5 4
      panda/src/pgraph/renderState.cxx
  36. 4 3
      panda/src/pgraph/transparencyAttrib.cxx
  37. 1 1
      panda/src/pgraph/transparencyAttrib.h
  38. 24 2
      panda/src/pgraphnodes/pointLight.I
  39. 13 2
      panda/src/pgraphnodes/pointLight.cxx
  40. 5 0
      panda/src/pgraphnodes/pointLight.h
  41. 1 0
      panda/src/pgraphnodes/shaderGenerator.cxx
  42. 24 2
      panda/src/pgraphnodes/spotlight.I
  43. 13 2
      panda/src/pgraphnodes/spotlight.cxx
  44. 5 0
      panda/src/pgraphnodes/spotlight.h
  45. 115 6
      panda/src/pnmimage/pfmFile.cxx
  46. 3 0
      panda/src/pnmimage/pfmFile.h
  47. 2 2
      panda/src/pnmimage/pnmImage.cxx
  48. 34 35
      panda/src/putil/bam.h
  49. 2 1
      panda/src/putil/bamReader.cxx
  50. 16 0
      panda/src/putil/bamWriter.I
  51. 4 0
      panda/src/putil/bamWriter.cxx
  52. 4 0
      panda/src/putil/bamWriter.h
  53. 7 0
      panda/src/putil/config_util.cxx
  54. 1 0
      panda/src/putil/config_util.h
  55. 8 0
      panda/src/putil/loaderOptions.cxx
  56. 1 0
      panda/src/putil/loaderOptions.h
  57. 15 0
      panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -567,7 +567,9 @@ protected:
            Texture::ComponentType type,
            Texture::ComponentType type,
            Texture::CompressionMode compression, int n);
            Texture::CompressionMode compression, int n);
 
 
+#ifdef SUPPORT_FIXED_FUNCTION
   void do_point_size();
   void do_point_size();
+#endif
 
 
   enum AutoAntialiasMode {
   enum AutoAntialiasMode {
     AA_poly,
     AA_poly,

+ 21 - 18
panda/src/glstuff/glShaderContext_src.cxx

@@ -1462,24 +1462,27 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
         return;
         return;
       }
       }
 #ifndef OPENGLES
 #ifndef OPENGLES
-      case GL_IMAGE_1D_EXT:
-      case GL_IMAGE_2D_EXT:
-      case GL_IMAGE_3D_EXT:
-      case GL_IMAGE_CUBE_EXT:
-      case GL_IMAGE_2D_ARRAY_EXT:
-      case GL_IMAGE_BUFFER_EXT:
-      case GL_INT_IMAGE_1D_EXT:
-      case GL_INT_IMAGE_2D_EXT:
-      case GL_INT_IMAGE_3D_EXT:
-      case GL_INT_IMAGE_CUBE_EXT:
-      case GL_INT_IMAGE_2D_ARRAY_EXT:
-      case GL_INT_IMAGE_BUFFER_EXT:
-      case GL_UNSIGNED_INT_IMAGE_1D_EXT:
-      case GL_UNSIGNED_INT_IMAGE_2D_EXT:
-      case GL_UNSIGNED_INT_IMAGE_3D_EXT:
-      case GL_UNSIGNED_INT_IMAGE_CUBE_EXT:
-      case GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT:
-      case GL_UNSIGNED_INT_IMAGE_BUFFER_EXT:
+      case GL_IMAGE_1D:
+      case GL_IMAGE_2D:
+      case GL_IMAGE_3D:
+      case GL_IMAGE_CUBE:
+      case GL_IMAGE_2D_ARRAY:
+      case GL_IMAGE_CUBE_MAP_ARRAY:
+      case GL_IMAGE_BUFFER:
+      case GL_INT_IMAGE_1D:
+      case GL_INT_IMAGE_2D:
+      case GL_INT_IMAGE_3D:
+      case GL_INT_IMAGE_CUBE:
+      case GL_INT_IMAGE_2D_ARRAY:
+      case GL_INT_IMAGE_CUBE_MAP_ARRAY:
+      case GL_INT_IMAGE_BUFFER:
+      case GL_UNSIGNED_INT_IMAGE_1D:
+      case GL_UNSIGNED_INT_IMAGE_2D:
+      case GL_UNSIGNED_INT_IMAGE_3D:
+      case GL_UNSIGNED_INT_IMAGE_CUBE:
+      case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+      case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
+      case GL_UNSIGNED_INT_IMAGE_BUFFER:
         // This won't really change at runtime, so we might as well bind once
         // This won't really change at runtime, so we might as well bind once
         // and then forget about it.
         // and then forget about it.
         _glgsg->_glUniform1i(p, _glsl_img_inputs.size());
         _glgsg->_glUniform1i(p, _glsl_img_inputs.size());

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

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

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

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

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

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

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

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

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

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

+ 5 - 2
panda/src/gobj/texture.I

@@ -1678,9 +1678,12 @@ clear_ram_mipmap_images() {
  */
  */
 INLINE void Texture::
 INLINE void Texture::
 generate_ram_mipmap_images() {
 generate_ram_mipmap_images() {
-  CDWriter cdata(_cycler, unlocked_ensure_ram_image(false));
+  // Don't use unlocked_ensure_ram_image here, because
+  // do_generate_ram_mipmap_images will want to decompress and recompress the
+  // image itself.
+  CDWriter cdata(_cycler, false);
   cdata->inc_image_modified();
   cdata->inc_image_modified();
-  do_generate_ram_mipmap_images(cdata);
+  do_generate_ram_mipmap_images(cdata, true);
 }
 }
 
 
 /**
 /**

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

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

+ 1 - 1
panda/src/gobj/texture.h

@@ -697,7 +697,7 @@ protected:
   INLINE void do_clear_ram_image(CData *cdata);
   INLINE void do_clear_ram_image(CData *cdata);
   void do_clear_simple_ram_image(CData *cdata);
   void do_clear_simple_ram_image(CData *cdata);
   void do_clear_ram_mipmap_images(CData *cdata);
   void do_clear_ram_mipmap_images(CData *cdata);
-  void do_generate_ram_mipmap_images(CData *cdata);
+  void do_generate_ram_mipmap_images(CData *cdata, bool allow_recompress);
   void do_set_pad_size(CData *cdata, int x, int y, int z);
   void do_set_pad_size(CData *cdata, int x, int y, int z);
   virtual bool do_can_reload(const CData *cdata) const;
   virtual bool do_can_reload(const CData *cdata) const;
   bool do_reload(CData *cdata);
   bool do_reload(CData *cdata);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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