Browse Source

Merge branch 'master' into input-overhaul

rdb 9 years ago
parent
commit
8434914ab6
100 changed files with 3194 additions and 449 deletions
  1. 4 0
      direct/src/directscripts/eggcacher.py
  2. 3 0
      direct/src/directscripts/packpanda.py
  3. 36 1
      direct/src/showbase/PythonUtil.py
  4. 8 0
      doc/ReleaseNotes
  5. 6 3
      dtool/src/cppparser/cppFunctionType.cxx
  6. 1 1
      dtool/src/cppparser/cppFunctionType.h
  7. 32 1
      dtool/src/cppparser/cppPreprocessor.cxx
  8. 3 0
      dtool/src/cppparser/cppPreprocessor.h
  9. 1 0
      dtool/src/cppparser/cppScope.cxx
  10. 18 37
      dtool/src/cppparser/cppStructType.cxx
  11. 1 0
      dtool/src/cppparser/cppType.cxx
  12. 9 2
      dtool/src/dtoolbase/deletedBufferChain.cxx
  13. 1 5
      dtool/src/dtoolbase/deletedBufferChain.h
  14. 1 1
      dtool/src/dtoolbase/dtoolbase.h
  15. 16 4
      dtool/src/dtoolbase/memoryHook.I
  16. 5 0
      dtool/src/dtoolbase/memoryHook.cxx
  17. 2 0
      dtool/src/dtoolbase/neverFreeMemory.I
  18. 3 7
      dtool/src/dtoolbase/neverFreeMemory.cxx
  19. 15 10
      dtool/src/dtoolutil/filename.I
  20. 8 5
      dtool/src/dtoolutil/filename.h
  21. 3 1
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  22. 42 33
      dtool/src/prc/encryptStreamBuf.cxx
  23. 2 4
      dtool/src/prc/encryptStreamBuf.h
  24. 3 0
      dtool/src/prc/notify.cxx
  25. 4 0
      dtool/src/pystub/pystub.cxx
  26. 29 3
      makepanda/installer.nsi
  27. 169 95
      makepanda/makepanda.py
  28. 51 6
      makepanda/makepandacore.py
  29. 598 0
      makepanda/makewheel.py
  30. 10 1
      panda/src/bullet/bulletBodyNode.cxx
  31. 1 1
      panda/src/bullet/bulletGhostNode.cxx
  32. 0 1
      panda/src/bullet/bulletRigidBodyNode.I
  33. 8 12
      panda/src/bullet/bulletRigidBodyNode.cxx
  34. 1 1
      panda/src/bullet/bulletRigidBodyNode.h
  35. 14 7
      panda/src/cocoadisplay/cocoaGraphicsPipe.mm
  36. 3 1
      panda/src/display/frameBufferProperties.cxx
  37. 0 13
      panda/src/display/get_x11.h
  38. 8 0
      panda/src/display/graphicsStateGuardian.I
  39. 118 35
      panda/src/display/graphicsStateGuardian.cxx
  40. 9 1
      panda/src/display/graphicsStateGuardian.h
  41. 17 7
      panda/src/downloader/httpClient.cxx
  42. 18 2
      panda/src/dxgsg9/wdxGraphicsPipe9.cxx
  43. 1 0
      panda/src/egg2pg/save_egg_file.cxx
  44. 119 0
      panda/src/express/filename_ext.cxx
  45. 3 0
      panda/src/express/filename_ext.h
  46. 8 0
      panda/src/ffmpeg/config_ffmpeg.cxx
  47. 1 0
      panda/src/ffmpeg/config_ffmpeg.h
  48. 0 2
      panda/src/ffmpeg/ffmpegAudioCursor.cxx
  49. 0 5
      panda/src/ffmpeg/ffmpegAudioCursor.h
  50. 48 18
      panda/src/ffmpeg/ffmpegVideoCursor.cxx
  51. 2 0
      panda/src/ffmpeg/ffmpegVideoCursor.h
  52. 25 0
      panda/src/glstuff/glBufferContext_src.I
  53. 49 0
      panda/src/glstuff/glBufferContext_src.cxx
  54. 52 0
      panda/src/glstuff/glBufferContext_src.h
  55. 205 41
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  56. 16 2
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  57. 59 3
      panda/src/glstuff/glShaderContext_src.cxx
  58. 12 0
      panda/src/glstuff/glShaderContext_src.h
  59. 6 0
      panda/src/glstuff/glmisc_src.cxx
  60. 1 0
      panda/src/glstuff/glmisc_src.h
  61. 1 0
      panda/src/glstuff/glstuff_src.cxx
  62. 1 0
      panda/src/glstuff/glstuff_src.h
  63. 1 0
      panda/src/glxdisplay/panda_glxext.h
  64. 1 0
      panda/src/gobj/p3gobj_composite2.cxx
  65. 5 2
      panda/src/gobj/preparedGraphicsObjects.I
  66. 165 0
      panda/src/gobj/preparedGraphicsObjects.cxx
  67. 19 0
      panda/src/gobj/preparedGraphicsObjects.h
  68. 37 4
      panda/src/gobj/shader.cxx
  69. 63 0
      panda/src/gobj/shaderBuffer.I
  70. 193 0
      panda/src/gobj/shaderBuffer.cxx
  71. 100 0
      panda/src/gobj/shaderBuffer.h
  72. 1 0
      panda/src/gobj/shaderContext.h
  73. 146 24
      panda/src/gobj/texture.cxx
  74. 2 0
      panda/src/gobj/texture.h
  75. 1 1
      panda/src/grutil/config_grutil.cxx
  76. 5 0
      panda/src/gsgbase/graphicsStateGuardianBase.h
  77. 6 2
      panda/src/movies/dr_flac.h
  78. 1 0
      panda/src/pgraph/light.h
  79. 8 0
      panda/src/pgraph/nodePath.I
  80. 2 0
      panda/src/pgraph/nodePath.h
  81. 32 2
      panda/src/pgraph/shaderAttrib.cxx
  82. 1 0
      panda/src/pgraph/shaderAttrib.h
  83. 12 0
      panda/src/pgraph/shaderInput.I
  84. 6 1
      panda/src/pgraph/shaderInput.h
  85. 3 0
      panda/src/pgraphnodes/config_pgraphnodes.cxx
  86. 1 0
      panda/src/pgraphnodes/p3pgraphnodes_composite2.cxx
  87. 59 0
      panda/src/pgraphnodes/rectangleLight.I
  88. 150 0
      panda/src/pgraphnodes/rectangleLight.cxx
  89. 98 0
      panda/src/pgraphnodes/rectangleLight.h
  90. 18 0
      panda/src/pipeline/pythonThread.cxx
  91. 2 2
      panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx
  92. 3 3
      panda/src/pstatclient/pStatProperties.cxx
  93. 16 3
      panda/src/putil/cachedTypedWritableReferenceCount.I
  94. 4 1
      panda/src/putil/cachedTypedWritableReferenceCount.h
  95. 17 0
      panda/src/putil/copyOnWriteObject.cxx
  96. 3 0
      panda/src/putil/copyOnWriteObject.h
  97. 86 11
      panda/src/putil/copyOnWritePointer.I
  98. 12 16
      panda/src/putil/copyOnWritePointer.cxx
  99. 9 5
      panda/src/putil/copyOnWritePointer.h
  100. 16 0
      panda/src/vision/openCVTexture.cxx

+ 4 - 0
direct/src/directscripts/eggcacher.py

@@ -88,3 +88,7 @@ class EggCacher:
             progress += size
             progress += size
 
 
 cacher = EggCacher(sys.argv[1:])
 cacher = EggCacher(sys.argv[1:])
+
+# Dummy main function so this can be added to console_scripts.
+def main():
+    return 0

+ 3 - 0
direct/src/directscripts/packpanda.py

@@ -419,3 +419,6 @@ else:
     if not(os.path.exists("/usr/bin/rpmbuild") or os.path.exists("/usr/bin/dpkg-deb")):
     if not(os.path.exists("/usr/bin/rpmbuild") or os.path.exists("/usr/bin/dpkg-deb")):
         exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!")
         exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!")
 
 
+# Dummy main function so this can be added to console_scripts.
+def main():
+    return 0

+ 36 - 1
direct/src/showbase/PythonUtil.py

@@ -38,7 +38,6 @@ import os
 import sys
 import sys
 import random
 import random
 import time
 import time
-import importlib
 
 
 __report_indent = 3
 __report_indent = 3
 
 
@@ -61,6 +60,42 @@ def Functor(function, *args, **kArgs):
     return functor
     return functor
 """
 """
 
 
+try:
+    import importlib
+except ImportError:
+    # Backward compatibility for Python 2.6.
+    def _resolve_name(name, package, level):
+        if not hasattr(package, 'rindex'):
+            raise ValueError("'package' not set to a string")
+        dot = len(package)
+        for x in xrange(level, 1, -1):
+            try:
+                dot = package.rindex('.', 0, dot)
+            except ValueError:
+                raise ValueError("attempted relative import beyond top-level "
+                                  "package")
+        return "%s.%s" % (package[:dot], name)
+
+    def import_module(name, package=None):
+        if name.startswith('.'):
+            if not package:
+                raise TypeError("relative imports require the 'package' argument")
+            level = 0
+            for character in name:
+                if character != '.':
+                    break
+                level += 1
+            name = _resolve_name(name[level:], package, level)
+        __import__(name)
+        return sys.modules[name]
+
+    imp = import_module('imp')
+    importlib = imp.new_module("importlib")
+    importlib._resolve_name = _resolve_name
+    importlib.import_module = import_module
+    sys.modules['importlib'] = importlib
+
+
 class Functor:
 class Functor:
     def __init__(self, function, *args, **kargs):
     def __init__(self, function, *args, **kargs):
         assert callable(function), "function should be a callable obj"
         assert callable(function), "function should be a callable obj"

+ 8 - 0
doc/ReleaseNotes

@@ -2,6 +2,7 @@
 
 
 This issue fixes several bugs that were still found in 1.9.2.
 This issue fixes several bugs that were still found in 1.9.2.
 
 
+* Fix crash when using homebrew Python on Mac OS X
 * Fix crash when running in Steam on Linux when using OpenAL
 * Fix crash when running in Steam on Linux when using OpenAL
 * Fix crash using wx/tkinter on Mac as long as want-wx/tk is set
 * Fix crash using wx/tkinter on Mac as long as want-wx/tk is set
 * Fix loading models from 'models' package with models/ prefix
 * Fix loading models from 'models' package with models/ prefix
@@ -47,6 +48,13 @@ This issue fixes several bugs that were still found in 1.9.2.
 * Fix compilation errors with Bullet 2.84
 * Fix compilation errors with Bullet 2.84
 * Fix exception when trying to pickle NodePathCollection objects
 * Fix exception when trying to pickle NodePathCollection objects
 * Fix error when trying to raise vectors to a power
 * Fix error when trying to raise vectors to a power
+* GLSL: fix error when legacy matrix generator inputs are mat3
+* Now tries to preserve refresh rate when switching fullscreen on Windows
+* Fix back-to-front sorting when gl-coordinate-system is changed
+* Now also compiles on older Linux distros (eg. CentOS 5 / manylinux1)
+* get_keyboard_map now includes keys on layouts with special characters
+* Fix crash due to incorrect alignment when compiling Eigen with AVX
+* Fix crash when writing 16-bit .tif file (now silently downsamples)
 
 
 ------------------------  RELEASE 1.9.2  ------------------------
 ------------------------  RELEASE 1.9.2  ------------------------
 
 

+ 6 - 3
dtool/src/cppparser/cppFunctionType.cxx

@@ -326,14 +326,17 @@ as_function_type() {
  * This is similar to is_equal(), except it is more forgiving: it considers
  * This is similar to is_equal(), except it is more forgiving: it considers
  * the functions to be equivalent only if the return type and the types of all
  * the functions to be equivalent only if the return type and the types of all
  * parameters match.
  * parameters match.
+ *
+ * Note that this isn't symmetric to account for covariant return types.
  */
  */
 bool CPPFunctionType::
 bool CPPFunctionType::
-is_equivalent_function(const CPPFunctionType &other) const {
-  if (!_return_type->is_equivalent(*other._return_type)) {
+match_virtual_override(const CPPFunctionType &other) const {
+  if (!_return_type->is_equivalent(*other._return_type) &&
+      !_return_type->is_convertible_to(other._return_type)) {
     return false;
     return false;
   }
   }
 
 
-  if (_flags != other._flags) {
+  if (((_flags ^ other._flags) & ~(F_override | F_final)) != 0) {
     return false;
     return false;
   }
   }
 
 

+ 1 - 1
dtool/src/cppparser/cppFunctionType.h

@@ -84,7 +84,7 @@ public:
 
 
   virtual CPPFunctionType *as_function_type();
   virtual CPPFunctionType *as_function_type();
 
 
-  bool is_equivalent_function(const CPPFunctionType &other) const;
+  bool match_virtual_override(const CPPFunctionType &other) const;
 
 
   CPPIdentifier *_class_owner;
   CPPIdentifier *_class_owner;
 
 

+ 32 - 1
dtool/src/cppparser/cppPreprocessor.cxx

@@ -1461,7 +1461,6 @@ handle_define_directive(const string &args, const YYLTYPE &loc) {
       CPPManifest *other = result.first->second;
       CPPManifest *other = result.first->second;
       warning("redefinition of macro '" + manifest->_name + "'", loc);
       warning("redefinition of macro '" + manifest->_name + "'", loc);
       warning("previous definition is here", other->_loc);
       warning("previous definition is here", other->_loc);
-      delete other;
       result.first->second = manifest;
       result.first->second = manifest;
     }
     }
   }
   }
@@ -1679,6 +1678,38 @@ handle_pragma_directive(const string &args, const YYLTYPE &loc) {
     assert(it != _parsed_files.end());
     assert(it != _parsed_files.end());
     it->_pragma_once = true;
     it->_pragma_once = true;
   }
   }
+
+  char macro[64];
+  if (sscanf(args.c_str(), "push_macro ( \"%63[^\"]\" )", macro) == 1) {
+    // We just mark it as pushed for now, so that the next time someone tries
+    // to override it, we save the old value.
+    Manifests::iterator mi = _manifests.find(macro);
+    if (mi != _manifests.end()) {
+      _manifest_stack[macro].push_back(mi->second);
+    } else {
+      _manifest_stack[macro].push_back(NULL);
+    }
+
+  } else if (sscanf(args.c_str(), "pop_macro ( \"%63[^\"]\" )", macro) == 1) {
+    ManifestStack &stack = _manifest_stack[macro];
+    if (stack.size() > 0) {
+      CPPManifest *manifest = stack.back();
+      stack.pop_back();
+      Manifests::iterator mi = _manifests.find(macro);
+      if (manifest == NULL) {
+        // It was undefined when it was pushed, so make it undefined again.
+        if (mi != _manifests.end()) {
+          _manifests.erase(mi);
+        }
+      } else if (mi != _manifests.end()) {
+        mi->second = manifest;
+      } else {
+        _manifests.insert(Manifests::value_type(macro, manifest));
+      }
+    } else {
+      warning("pop_macro without matching push_macro", loc);
+    }
+  }
 }
 }
 
 
 /**
 /**

+ 3 - 0
dtool/src/cppparser/cppPreprocessor.h

@@ -72,6 +72,9 @@ public:
   typedef map<string, CPPManifest *> Manifests;
   typedef map<string, CPPManifest *> Manifests;
   Manifests _manifests;
   Manifests _manifests;
 
 
+  typedef pvector<CPPManifest *> ManifestStack;
+  map<string, ManifestStack> _manifest_stack;
+
   pvector<CPPFile::Source> _quote_include_kind;
   pvector<CPPFile::Source> _quote_include_kind;
   DSearchPath _quote_include_path;
   DSearchPath _quote_include_path;
   DSearchPath _angle_include_path;
   DSearchPath _angle_include_path;

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

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

+ 18 - 37
dtool/src/cppparser/cppStructType.cxx

@@ -13,6 +13,7 @@
 
 
 #include "cppStructType.h"
 #include "cppStructType.h"
 #include "cppTypedefType.h"
 #include "cppTypedefType.h"
+#include "cppReferenceType.h"
 #include "cppScope.h"
 #include "cppScope.h"
 #include "cppTypeProxy.h"
 #include "cppTypeProxy.h"
 #include "cppTemplateScope.h"
 #include "cppTemplateScope.h"
@@ -377,6 +378,10 @@ is_constructible(const CPPType *given_type) const {
     }
     }
   }
   }
 
 
+  if (is_abstract()) {
+    return false;
+  }
+
   // Check for a different constructor.
   // Check for a different constructor.
   CPPFunctionGroup *fgroup = get_constructor();
   CPPFunctionGroup *fgroup = get_constructor();
   if (fgroup != (CPPFunctionGroup *)NULL) {
   if (fgroup != (CPPFunctionGroup *)NULL) {
@@ -443,6 +448,10 @@ is_destructible() const {
  */
  */
 bool CPPStructType::
 bool CPPStructType::
 is_default_constructible(CPPVisibility min_vis) const {
 is_default_constructible(CPPVisibility min_vis) const {
+  if (is_abstract()) {
+    return false;
+  }
+
   CPPInstance *constructor = get_default_constructor();
   CPPInstance *constructor = get_default_constructor();
   if (constructor != (CPPInstance *)NULL) {
   if (constructor != (CPPInstance *)NULL) {
     // It has a default constructor.
     // It has a default constructor.
@@ -497,24 +506,6 @@ is_default_constructible(CPPVisibility min_vis) const {
     }
     }
   }
   }
 
 
-  // Check that we don't have pure virtual methods.
-  CPPScope::Functions::const_iterator fi;
-  for (fi = _scope->_functions.begin();
-       fi != _scope->_functions.end();
-       ++fi) {
-    CPPFunctionGroup *fgroup = (*fi).second;
-    CPPFunctionGroup::Instances::const_iterator ii;
-    for (ii = fgroup->_instances.begin();
-         ii != fgroup->_instances.end();
-         ++ii) {
-      CPPInstance *inst = (*ii);
-      if (inst->_storage_class & CPPInstance::SC_pure_virtual) {
-        // Here's a pure virtual function.
-        return false;
-      }
-    }
-  }
-
   return true;
   return true;
 }
 }
 
 
@@ -523,6 +514,10 @@ is_default_constructible(CPPVisibility min_vis) const {
  */
  */
 bool CPPStructType::
 bool CPPStructType::
 is_copy_constructible(CPPVisibility min_vis) const {
 is_copy_constructible(CPPVisibility min_vis) const {
+  if (is_abstract()) {
+    return false;
+  }
+
   CPPInstance *constructor = get_copy_constructor();
   CPPInstance *constructor = get_copy_constructor();
   if (constructor != (CPPInstance *)NULL) {
   if (constructor != (CPPInstance *)NULL) {
     // It has a copy constructor.
     // It has a copy constructor.
@@ -580,24 +575,6 @@ is_copy_constructible(CPPVisibility min_vis) const {
     }
     }
   }
   }
 
 
-  // Check that we don't have pure virtual methods.
-  CPPScope::Functions::const_iterator fi;
-  for (fi = _scope->_functions.begin();
-       fi != _scope->_functions.end();
-       ++fi) {
-    CPPFunctionGroup *fgroup = (*fi).second;
-    CPPFunctionGroup::Instances::const_iterator ii;
-    for (ii = fgroup->_instances.begin();
-         ii != fgroup->_instances.end();
-         ++ii) {
-      CPPInstance *inst = (*ii);
-      if (inst->_storage_class & CPPInstance::SC_pure_virtual) {
-        // Here's a pure virtual function.
-        return false;
-      }
-    }
-  }
-
   return true;
   return true;
 }
 }
 
 
@@ -619,6 +596,10 @@ is_move_constructible(CPPVisibility min_vis) const {
       return false;
       return false;
     }
     }
 
 
+    if (is_abstract()) {
+      return false;
+    }
+
     return true;
     return true;
   }
   }
 
 
@@ -1213,7 +1194,7 @@ get_virtual_funcs(VFunctions &funcs) const {
           CPPFunctionType *new_ftype = new_inst->_type->as_function_type();
           CPPFunctionType *new_ftype = new_inst->_type->as_function_type();
           assert(new_ftype != (CPPFunctionType *)NULL);
           assert(new_ftype != (CPPFunctionType *)NULL);
 
 
-          if (new_ftype->is_equivalent_function(*base_ftype)) {
+          if (new_ftype->match_virtual_override(*base_ftype)) {
             // It's a match!  We now know it's virtual.  Erase this function
             // It's a match!  We now know it's virtual.  Erase this function
             // from the list, so we can add it back in below.
             // from the list, so we can add it back in below.
             funcs.erase(vfi);
             funcs.erase(vfi);

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

@@ -16,6 +16,7 @@
 #include "cppPointerType.h"
 #include "cppPointerType.h"
 #include "cppReferenceType.h"
 #include "cppReferenceType.h"
 #include "cppStructType.h"
 #include "cppStructType.h"
+#include "cppTypedefType.h"
 #include "cppExtensionType.h"
 #include "cppExtensionType.h"
 #include <algorithm>
 #include <algorithm>
 
 

+ 9 - 2
dtool/src/dtoolbase/deletedBufferChain.cxx

@@ -39,7 +39,7 @@ allocate(size_t size, TypeHandle type_handle) {
   assert(size <= _buffer_size);
   assert(size <= _buffer_size);
 
 
   // Determine how much space to allocate.
   // Determine how much space to allocate.
-  const size_t alloc_size = _buffer_size + flag_reserved_bytes;
+  const size_t alloc_size = _buffer_size + flag_reserved_bytes + MemoryHook::get_memory_alignment() - 1;
 
 
   ObjectNode *obj;
   ObjectNode *obj;
 
 
@@ -69,7 +69,10 @@ allocate(size_t size, TypeHandle type_handle) {
   // If we get here, the deleted_chain is empty; we have to allocate a new
   // If we get here, the deleted_chain is empty; we have to allocate a new
   // object from the system pool.
   // object from the system pool.
 
 
-  obj = (ObjectNode *)NeverFreeMemory::alloc(alloc_size);
+  // Allocate memory, and make sure the object starts at the proper alignment.
+  void *mem = NeverFreeMemory::alloc(alloc_size);
+  intptr_t pad = ((intptr_t)flag_reserved_bytes - (intptr_t)mem) % MemoryHook::get_memory_alignment();
+  obj = (ObjectNode *)((uintptr_t)mem + pad);
 
 
 #ifdef USE_DELETEDCHAINFLAG
 #ifdef USE_DELETEDCHAINFLAG
   obj->_flag = DCF_alive;
   obj->_flag = DCF_alive;
@@ -77,6 +80,10 @@ allocate(size_t size, TypeHandle type_handle) {
 
 
   void *ptr = node_to_buffer(obj);
   void *ptr = node_to_buffer(obj);
 
 
+#ifdef _DEBUG
+  assert(((uintptr_t)ptr % MemoryHook::get_memory_alignment()) == 0);
+#endif
+
 #ifdef DO_MEMORY_USAGE
 #ifdef DO_MEMORY_USAGE
   type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size);
   type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size);
 #endif  // DO_MEMORY_USAGE
 #endif  // DO_MEMORY_USAGE

+ 1 - 5
dtool/src/dtoolbase/deletedBufferChain.h

@@ -95,12 +95,8 @@ private:
   // Without DELETEDCHAINFLAG, we don't even store the _flag member at all.
   // Without DELETEDCHAINFLAG, we don't even store the _flag member at all.
   static const size_t flag_reserved_bytes = 0;
   static const size_t flag_reserved_bytes = 0;
 
 
-#elif defined(LINMATH_ALIGN)
-  // With SSE2 alignment, we need all 16 bytes to preserve alignment.
-  static const size_t flag_reserved_bytes = 16;
-
 #else
 #else
-  // Otherwise, we only need enough space for the Integer itself.
+  // Otherwise, we need space for the integer.
   static const size_t flag_reserved_bytes = sizeof(AtomicAdjust::Integer);
   static const size_t flag_reserved_bytes = sizeof(AtomicAdjust::Integer);
 #endif  // USE_DELETEDCHAINFLAG
 #endif  // USE_DELETEDCHAINFLAG
 
 

+ 1 - 1
dtool/src/dtoolbase/dtoolbase.h

@@ -374,7 +374,7 @@ typedef struct _object PyObject;
 // externally.
 // externally.
 #define MEMORY_HOOK_DO_ALIGN 1
 #define MEMORY_HOOK_DO_ALIGN 1
 
 
-#elif defined(IS_OSX) || defined(_WIN64)
+#elif (defined(IS_OSX) || defined(_WIN64)) && !defined(__AVX__)
 // The OS-provided malloc implementation will do the required alignment.
 // The OS-provided malloc implementation will do the required alignment.
 #undef MEMORY_HOOK_DO_ALIGN
 #undef MEMORY_HOOK_DO_ALIGN
 
 

+ 16 - 4
dtool/src/dtoolbase/memoryHook.I

@@ -43,10 +43,16 @@ get_memory_alignment() {
 #ifdef LINMATH_ALIGN
 #ifdef LINMATH_ALIGN
   // We require 16-byte alignment of certain structures, to support SSE2.  We
   // We require 16-byte alignment of certain structures, to support SSE2.  We
   // don't strictly have to align *everything*, but it's just easier to do so.
   // don't strictly have to align *everything*, but it's just easier to do so.
+#ifdef __AVX__
+  // Eigen requires 32-byte alignment when using AVX instructions.
+  const size_t alignment_size = 32;
+#else
   const size_t alignment_size = 16;
   const size_t alignment_size = 16;
+#endif
 #else
 #else
-  // Otherwise, use word alignment.
-  const size_t alignment_size = sizeof(void *);
+  // Otherwise, align to two words.  This seems to be pretty standard to the
+  // point where some code may rely on this being the case.
+  const size_t alignment_size = sizeof(void *) * 2;
 #endif
 #endif
   return alignment_size;
   return alignment_size;
 }
 }
@@ -64,7 +70,12 @@ get_header_reserved_bytes() {
 #ifdef LINMATH_ALIGN
 #ifdef LINMATH_ALIGN
   // If we're doing SSE2 alignment, we must reserve a full 16-byte block,
   // If we're doing SSE2 alignment, we must reserve a full 16-byte block,
   // since anything less than that will spoil the alignment.
   // since anything less than that will spoil the alignment.
+#ifdef __AVX__
+  // Eigen requires 32-byte alignment when using AVX instructions.
+  static const size_t header_reserved_bytes = 32;
+#else
   static const size_t header_reserved_bytes = 16;
   static const size_t header_reserved_bytes = 16;
+#endif
 
 
 #elif defined(MEMORY_HOOK_DO_ALIGN)
 #elif defined(MEMORY_HOOK_DO_ALIGN)
   // If we're just aligning to words, we reserve a block as big as two words,
   // If we're just aligning to words, we reserve a block as big as two words,
@@ -72,8 +83,9 @@ get_header_reserved_bytes() {
   static const size_t header_reserved_bytes = sizeof(size_t) + sizeof(size_t);
   static const size_t header_reserved_bytes = sizeof(size_t) + sizeof(size_t);
 
 
 #else
 #else
-  // If we're not aligning, we just need space for the word itself.
-  static const size_t header_reserved_bytes = sizeof(size_t);
+  // Virtually all allocators align to two words, so we make sure we preserve
+  // that alignment for the benefit of anyone who relies upon that.
+  static const size_t header_reserved_bytes = sizeof(void *) * 2;
 #endif
 #endif
 
 
   return header_reserved_bytes;
   return header_reserved_bytes;

+ 5 - 0
dtool/src/dtoolbase/memoryHook.cxx

@@ -53,8 +53,13 @@
 // drose: We require 16-byte alignment of certain structures, to
 // drose: We require 16-byte alignment of certain structures, to
 // support SSE2.  We don't strictly have to align *everything*, but
 // support SSE2.  We don't strictly have to align *everything*, but
 // it's just easier to do so.
 // it's just easier to do so.
+#ifdef __AVX__
+// Eigen requires 32-byte alignment when using AVX instructions.
+#define MALLOC_ALIGNMENT ((size_t)32U)
+#else
 #define MALLOC_ALIGNMENT ((size_t)16U)
 #define MALLOC_ALIGNMENT ((size_t)16U)
 #endif
 #endif
+#endif
 
 
 #include "dlmalloc_src.cxx"
 #include "dlmalloc_src.cxx"
 
 

+ 2 - 0
dtool/src/dtoolbase/neverFreeMemory.I

@@ -14,6 +14,8 @@
 /**
 /**
  * Returns a pointer to a newly-allocated block of memory of the indicated
  * Returns a pointer to a newly-allocated block of memory of the indicated
  * size.
  * size.
+ *
+ * Please note that the resulting pointer is not aligned to any boundary.
  */
  */
 INLINE void *NeverFreeMemory::
 INLINE void *NeverFreeMemory::
 alloc(size_t size) {
 alloc(size_t size) {

+ 3 - 7
dtool/src/dtoolbase/neverFreeMemory.cxx

@@ -39,13 +39,9 @@ void *NeverFreeMemory::
 ns_alloc(size_t size) {
 ns_alloc(size_t size) {
   _lock.acquire();
   _lock.acquire();
 
 
-  // We always allocate integer multiples of this many bytes, to guarantee
-  // this minimum alignment.
-  static const size_t alignment_size = MemoryHook::get_memory_alignment();
-
-  // Round up to the next alignment_size.
-  size = ((size + alignment_size - 1) / alignment_size) * alignment_size;
-
+  //NB: we no longer do alignment here.  The only class that uses this is
+  // DeletedBufferChain, and we can do the alignment potentially more
+  // efficiently there since we don't end up overallocating as much.
   _total_used += size;
   _total_used += size;
 
 
   // Look for a page that has sufficient space remaining.
   // Look for a page that has sufficient space remaining.

+ 15 - 10
dtool/src/dtoolutil/filename.I

@@ -38,7 +38,6 @@ Filename(const char *filename) {
   (*this) = filename;
   (*this) = filename;
 }
 }
 
 
-
 /**
 /**
  *
  *
  */
  */
@@ -84,6 +83,20 @@ Filename(Filename &&from) NOEXCEPT :
 }
 }
 #endif  // USE_MOVE_SEMANTICS
 #endif  // USE_MOVE_SEMANTICS
 
 
+/**
+ * Creates an empty Filename.
+ */
+INLINE Filename::
+Filename() :
+  _dirname_end(0),
+  _basename_start(0),
+  _basename_end(string::npos),
+  _extension_start(string::npos),
+  _hash_start(string::npos),
+  _hash_end(string::npos),
+  _flags(0) {
+}
+
 /**
 /**
  *
  *
  */
  */
@@ -155,14 +168,6 @@ pattern_filename(const string &filename) {
   return result;
   return result;
 }
 }
 
 
-/**
- *
- */
-INLINE Filename::
-~Filename() {
-}
-
-
 /**
 /**
  *
  *
  */
  */
@@ -233,7 +238,7 @@ operator = (string &&filename) NOEXCEPT {
  */
  */
 INLINE Filename &Filename::
 INLINE Filename &Filename::
 operator = (Filename &&from) NOEXCEPT {
 operator = (Filename &&from) NOEXCEPT {
-  _filename = MOVE(from._filename);
+  _filename = move(from._filename);
   _dirname_end = from._dirname_end;
   _dirname_end = from._dirname_end;
   _basename_start = from._basename_start;
   _basename_start = from._basename_start;
   _basename_end = from._basename_end;
   _basename_end = from._basename_end;

+ 8 - 5
dtool/src/dtoolutil/filename.h

@@ -55,20 +55,22 @@ public:
   };
   };
 
 
   INLINE Filename(const char *filename);
   INLINE Filename(const char *filename);
-
-PUBLISHED:
-  INLINE Filename(const string &filename = "");
+  INLINE Filename(const string &filename);
   INLINE Filename(const wstring &filename);
   INLINE Filename(const wstring &filename);
   INLINE Filename(const Filename &copy);
   INLINE Filename(const Filename &copy);
-  Filename(const Filename &dirname, const Filename &basename);
-  INLINE ~Filename();
 
 
 #ifdef USE_MOVE_SEMANTICS
 #ifdef USE_MOVE_SEMANTICS
   INLINE Filename(string &&filename) NOEXCEPT;
   INLINE Filename(string &&filename) NOEXCEPT;
   INLINE Filename(Filename &&from) NOEXCEPT;
   INLINE Filename(Filename &&from) NOEXCEPT;
 #endif
 #endif
 
 
+PUBLISHED:
+  INLINE Filename();
+  Filename(const Filename &dirname, const Filename &basename);
+
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
+  EXTENSION(Filename(PyObject *path));
+
   EXTENSION(PyObject *__reduce__(PyObject *self) const);
   EXTENSION(PyObject *__reduce__(PyObject *self) const);
 #endif
 #endif
 
 
@@ -118,6 +120,7 @@ PUBLISHED:
   INLINE char operator [] (size_t n) const;
   INLINE char operator [] (size_t n) const;
 
 
   EXTENSION(PyObject *__repr__() const);
   EXTENSION(PyObject *__repr__() const);
+  EXTENSION(PyObject *__fspath__() const);
 
 
   INLINE string substr(size_t begin) const;
   INLINE string substr(size_t begin) const;
   INLINE string substr(size_t begin, size_t end) const;
   INLINE string substr(size_t begin, size_t end) const;

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

@@ -6142,7 +6142,7 @@ pack_return_value(ostream &out, int indent_level, FunctionRemap *remap,
 
 
         write_python_instance(out, indent_level, return_expr, owns_memory, itype, is_const);
         write_python_instance(out, indent_level, return_expr, owns_memory, itype, is_const);
       }
       }
-    } else if (TypeManager::is_struct(orig_type->as_pointer_type()->_pointing_at)) {
+    } else if (TypeManager::is_struct(orig_type->remove_pointer())) {
       TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false);
       TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false);
       const InterrogateType &itype = idb->get_type(type_index);
       const InterrogateType &itype = idb->get_type(type_index);
 
 
@@ -6749,6 +6749,8 @@ is_cpp_type_legal(CPPType *in_ctype) {
     return true;
     return true;
   } else if (TypeManager::is_pointer_to_simple(type)) {
   } else if (TypeManager::is_pointer_to_simple(type)) {
     return true;
     return true;
+  } else if (builder.in_forcetype(type->get_local_name(&parser))) {
+    return true;
   } else if (TypeManager::is_exported(type)) {
   } else if (TypeManager::is_exported(type)) {
     return true;
     return true;
   } else if (TypeManager::is_pointer_to_PyObject(in_ctype)) {
   } else if (TypeManager::is_pointer_to_PyObject(in_ctype)) {

+ 42 - 33
dtool/src/prc/encryptStreamBuf.cxx

@@ -73,8 +73,8 @@ EncryptStreamBuf() {
   _key_length = encryption_key_length;
   _key_length = encryption_key_length;
   _iteration_count = encryption_iteration_count;
   _iteration_count = encryption_iteration_count;
 
 
-  _read_valid = false;
-  _write_valid = false;
+  _read_ctx = NULL;
+  _write_ctx = NULL;
 
 
   _read_overflow_buffer = NULL;
   _read_overflow_buffer = NULL;
   _in_read_overflow_buffer = 0;
   _in_read_overflow_buffer = 0;
@@ -110,7 +110,11 @@ open_read(istream *source, bool owns_source, const string &password) {
 
 
   _source = source;
   _source = source;
   _owns_source = owns_source;
   _owns_source = owns_source;
-  _read_valid = false;
+
+  if (_read_ctx != NULL) {
+    EVP_CIPHER_CTX_free(_read_ctx);
+    _read_ctx = NULL;
+  }
 
 
   // Now read the header information.
   // Now read the header information.
   StreamReader sr(_source, false);
   StreamReader sr(_source, false);
@@ -143,17 +147,21 @@ open_read(istream *source, bool owns_source, const string &password) {
 
 
   string iv = sr.extract_bytes(iv_length);
   string iv = sr.extract_bytes(iv_length);
 
 
+  _read_ctx = EVP_CIPHER_CTX_new();
+  nassertv(_read_ctx != NULL);
+
   // Initialize the context
   // Initialize the context
   int result;
   int result;
-  result = EVP_DecryptInit(&_read_ctx, cipher, NULL, (unsigned char *)iv.data());
+  result = EVP_DecryptInit(_read_ctx, cipher, NULL, (unsigned char *)iv.data());
   nassertv(result > 0);
   nassertv(result > 0);
 
 
-  result = EVP_CIPHER_CTX_set_key_length(&_read_ctx, key_length);
+  result = EVP_CIPHER_CTX_set_key_length(_read_ctx, key_length);
   if (result <= 0) {
   if (result <= 0) {
     prc_cat.error()
     prc_cat.error()
       << "Invalid key length " << key_length * 8 << " bits for algorithm "
       << "Invalid key length " << key_length * 8 << " bits for algorithm "
       << OBJ_nid2sn(nid) << "\n";
       << OBJ_nid2sn(nid) << "\n";
-    EVP_CIPHER_CTX_cleanup(&_read_ctx);
+    EVP_CIPHER_CTX_free(_read_ctx);
+    _read_ctx = NULL;
     return;
     return;
   }
   }
 
 
@@ -167,11 +175,9 @@ open_read(istream *source, bool owns_source, const string &password) {
   nassertv(result > 0);
   nassertv(result > 0);
 
 
   // Store the key within the context.
   // Store the key within the context.
-  result = EVP_DecryptInit(&_read_ctx, NULL, key, NULL);
+  result = EVP_DecryptInit(_read_ctx, NULL, key, NULL);
   nassertv(result > 0);
   nassertv(result > 0);
 
 
-  _read_valid = true;
-
   _read_overflow_buffer = new unsigned char[_read_block_size];
   _read_overflow_buffer = new unsigned char[_read_block_size];
   _in_read_overflow_buffer = 0;
   _in_read_overflow_buffer = 0;
   thread_consider_yield();
   thread_consider_yield();
@@ -182,9 +188,9 @@ open_read(istream *source, bool owns_source, const string &password) {
  */
  */
 void EncryptStreamBuf::
 void EncryptStreamBuf::
 close_read() {
 close_read() {
-  if (_read_valid) {
-    EVP_CIPHER_CTX_cleanup(&_read_ctx);
-    _read_valid = false;
+  if (_read_ctx != NULL) {
+    EVP_CIPHER_CTX_free(_read_ctx);
+    _read_ctx = NULL;
   }
   }
 
 
   if (_read_overflow_buffer != (unsigned char *)NULL) {
   if (_read_overflow_buffer != (unsigned char *)NULL) {
@@ -211,7 +217,6 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
   close_write();
   close_write();
   _dest = dest;
   _dest = dest;
   _owns_dest = owns_dest;
   _owns_dest = owns_dest;
-  _write_valid = false;
 
 
   const EVP_CIPHER *cipher =
   const EVP_CIPHER *cipher =
     EVP_get_cipherbyname(_algorithm.c_str());
     EVP_get_cipherbyname(_algorithm.c_str());
@@ -220,21 +225,23 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
     prc_cat.error()
     prc_cat.error()
       << "Unknown encryption algorithm: " << _algorithm << "\n";
       << "Unknown encryption algorithm: " << _algorithm << "\n";
     return;
     return;
-  };
+  }
 
 
   int nid = EVP_CIPHER_nid(cipher);
   int nid = EVP_CIPHER_nid(cipher);
 
 
   int iv_length = EVP_CIPHER_iv_length(cipher);
   int iv_length = EVP_CIPHER_iv_length(cipher);
   _write_block_size = EVP_CIPHER_block_size(cipher);
   _write_block_size = EVP_CIPHER_block_size(cipher);
 
 
-  unsigned char *iv = (unsigned char *)alloca(iv_length);
-
   // Generate a random IV.  It doesn't need to be cryptographically secure,
   // Generate a random IV.  It doesn't need to be cryptographically secure,
   // just unique.
   // just unique.
+  unsigned char *iv = (unsigned char *)alloca(iv_length);
   RAND_pseudo_bytes(iv, iv_length);
   RAND_pseudo_bytes(iv, iv_length);
 
 
+  _write_ctx = EVP_CIPHER_CTX_new();
+  nassertv(_write_ctx != NULL);
+
   int result;
   int result;
-  result = EVP_EncryptInit(&_write_ctx, cipher, NULL, iv);
+  result = EVP_EncryptInit(_write_ctx, cipher, NULL, iv);
   nassertv(result > 0);
   nassertv(result > 0);
 
 
   // Store the appropriate key length in the context.
   // Store the appropriate key length in the context.
@@ -242,12 +249,13 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
   if (key_length == 0) {
   if (key_length == 0) {
     key_length = EVP_CIPHER_key_length(cipher);
     key_length = EVP_CIPHER_key_length(cipher);
   }
   }
-  result = EVP_CIPHER_CTX_set_key_length(&_write_ctx, key_length);
+  result = EVP_CIPHER_CTX_set_key_length(_write_ctx, key_length);
   if (result <= 0) {
   if (result <= 0) {
     prc_cat.error()
     prc_cat.error()
       << "Invalid key length " << key_length * 8 << " bits for algorithm "
       << "Invalid key length " << key_length * 8 << " bits for algorithm "
       << OBJ_nid2sn(nid) << "\n";
       << OBJ_nid2sn(nid) << "\n";
-    EVP_CIPHER_CTX_cleanup(&_write_ctx);
+    EVP_CIPHER_CTX_free(_write_ctx);
+    _write_ctx = NULL;
     return;
     return;
   }
   }
 
 
@@ -271,7 +279,7 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
   nassertv(result > 0);
   nassertv(result > 0);
 
 
   // Store the key in the context.
   // Store the key in the context.
-  result = EVP_EncryptInit(&_write_ctx, NULL, key, NULL);
+  result = EVP_EncryptInit(_write_ctx, NULL, key, NULL);
   nassertv(result > 0);
   nassertv(result > 0);
 
 
   // Now write the header information to the stream.
   // Now write the header information to the stream.
@@ -284,7 +292,6 @@ open_write(ostream *dest, bool owns_dest, const string &password) {
   sw.add_uint16((uint16_t)count);
   sw.add_uint16((uint16_t)count);
   sw.append_data(iv, iv_length);
   sw.append_data(iv, iv_length);
 
 
-  _write_valid = true;
   thread_consider_yield();
   thread_consider_yield();
 }
 }
 
 
@@ -298,15 +305,16 @@ close_write() {
     write_chars(pbase(), n);
     write_chars(pbase(), n);
     pbump(-(int)n);
     pbump(-(int)n);
 
 
-    if (_write_valid) {
+    if (_write_ctx != NULL) {
       unsigned char *write_buffer = (unsigned char *)alloca(_write_block_size);
       unsigned char *write_buffer = (unsigned char *)alloca(_write_block_size);
       int bytes_written = 0;
       int bytes_written = 0;
-      EVP_EncryptFinal(&_write_ctx, write_buffer, &bytes_written);
+      EVP_EncryptFinal(_write_ctx, write_buffer, &bytes_written);
       thread_consider_yield();
       thread_consider_yield();
 
 
       _dest->write((const char *)write_buffer, bytes_written);
       _dest->write((const char *)write_buffer, bytes_written);
 
 
-      _write_valid = false;
+      EVP_CIPHER_CTX_free(_write_ctx);
+      _write_ctx = NULL;
     }
     }
 
 
     if (_owns_dest) {
     if (_owns_dest) {
@@ -418,7 +426,7 @@ read_chars(char *start, size_t length) {
 
 
   do {
   do {
     // Get more bytes from the stream.
     // Get more bytes from the stream.
-    if (!_read_valid) {
+    if (_read_ctx == NULL) {
       return 0;
       return 0;
     }
     }
 
 
@@ -429,20 +437,21 @@ read_chars(char *start, size_t length) {
     int result;
     int result;
     if (source_length != 0) {
     if (source_length != 0) {
       result =
       result =
-        EVP_DecryptUpdate(&_read_ctx, read_buffer, &bytes_read,
+        EVP_DecryptUpdate(_read_ctx, read_buffer, &bytes_read,
                           source_buffer, source_length);
                           source_buffer, source_length);
     } else {
     } else {
       result =
       result =
-        EVP_DecryptFinal(&_read_ctx, read_buffer, &bytes_read);
-      _read_valid = false;
+        EVP_DecryptFinal(_read_ctx, read_buffer, &bytes_read);
+      EVP_CIPHER_CTX_free(_read_ctx);
+      _read_ctx = NULL;
     }
     }
 
 
     if (result <= 0) {
     if (result <= 0) {
       prc_cat.error()
       prc_cat.error()
         << "Error decrypting stream.\n";
         << "Error decrypting stream.\n";
-      if (_read_valid) {
-        EVP_CIPHER_CTX_cleanup(&_read_ctx);
-        _read_valid = false;
+      if (_read_ctx != NULL) {
+        EVP_CIPHER_CTX_free(_read_ctx);
+        _read_ctx = NULL;
       }
       }
     }
     }
     thread_consider_yield();
     thread_consider_yield();
@@ -472,13 +481,13 @@ read_chars(char *start, size_t length) {
  */
  */
 void EncryptStreamBuf::
 void EncryptStreamBuf::
 write_chars(const char *start, size_t length) {
 write_chars(const char *start, size_t length) {
-  if (_write_valid && length != 0) {
+  if (_write_ctx != NULL && length != 0) {
     size_t max_write_buffer = length + _write_block_size;
     size_t max_write_buffer = length + _write_block_size;
     unsigned char *write_buffer = (unsigned char *)alloca(max_write_buffer);
     unsigned char *write_buffer = (unsigned char *)alloca(max_write_buffer);
 
 
     int bytes_written = 0;
     int bytes_written = 0;
     int result =
     int result =
-      EVP_EncryptUpdate(&_write_ctx, write_buffer, &bytes_written,
+      EVP_EncryptUpdate(_write_ctx, write_buffer, &bytes_written,
                         (unsigned char *)start, length);
                         (unsigned char *)start, length);
     if (result <= 0) {
     if (result <= 0) {
       prc_cat.error()
       prc_cat.error()

+ 2 - 4
dtool/src/prc/encryptStreamBuf.h

@@ -64,14 +64,12 @@ private:
   int _key_length;
   int _key_length;
   int _iteration_count;
   int _iteration_count;
 
 
-  bool _read_valid;
-  EVP_CIPHER_CTX _read_ctx;
+  EVP_CIPHER_CTX *_read_ctx;
   size_t _read_block_size;
   size_t _read_block_size;
   unsigned char *_read_overflow_buffer;
   unsigned char *_read_overflow_buffer;
   size_t _in_read_overflow_buffer;
   size_t _in_read_overflow_buffer;
 
 
-  bool _write_valid;
-  EVP_CIPHER_CTX _write_ctx;
+  EVP_CIPHER_CTX *_write_ctx;
   size_t _write_block_size;
   size_t _write_block_size;
 };
 };
 
 

+ 3 - 0
dtool/src/prc/notify.cxx

@@ -343,6 +343,9 @@ assert_failure(const char *expression, int line,
   // guarantee it has already been constructed.
   // guarantee it has already been constructed.
   ALIGN_16BYTE ConfigVariableBool assert_abort("assert-abort", false);
   ALIGN_16BYTE ConfigVariableBool assert_abort("assert-abort", false);
   if (assert_abort) {
   if (assert_abort) {
+    // Make sure the error message has been flushed to the output.
+    nout.flush();
+
 #ifdef WIN32
 #ifdef WIN32
     // How to trigger an exception in VC++ that offers to take us into the
     // How to trigger an exception in VC++ that offers to take us into the
     // debugger?  abort() doesn't do it.  We used to be able to assert(false),
     // debugger?  abort() doesn't do it.  We used to be able to assert(false),

+ 4 - 0
dtool/src/pystub/pystub.cxx

@@ -149,12 +149,14 @@ extern "C" {
   EXPCL_PYSTUB int PyUnicodeUCS2_FromStringAndSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_FromStringAndSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_FromWideChar(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_FromWideChar(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_AsWideChar(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_AsWideChar(...);
+  EXPCL_PYSTUB int PyUnicodeUCS2_AsWideCharString(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_GetSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS2_GetSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromFormat(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromFormat(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromString(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromString(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromStringAndSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromStringAndSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromWideChar(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_FromWideChar(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_AsWideChar(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_AsWideChar(...);
+  EXPCL_PYSTUB int PyUnicodeUCS4_AsWideCharString(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_GetSize(...);
   EXPCL_PYSTUB int PyUnicodeUCS4_GetSize(...);
   EXPCL_PYSTUB int PyUnicode_AsUTF8(...);
   EXPCL_PYSTUB int PyUnicode_AsUTF8(...);
   EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...);
   EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...);
@@ -349,12 +351,14 @@ int PyUnicodeUCS2_FromString(...) { return 0; }
 int PyUnicodeUCS2_FromStringAndSize(...) { return 0; }
 int PyUnicodeUCS2_FromStringAndSize(...) { return 0; }
 int PyUnicodeUCS2_FromWideChar(...) { return 0; }
 int PyUnicodeUCS2_FromWideChar(...) { return 0; }
 int PyUnicodeUCS2_AsWideChar(...) { return 0; }
 int PyUnicodeUCS2_AsWideChar(...) { return 0; }
+int PyUnicodeUCS2_AsWideCharString(...) { return 0; }
 int PyUnicodeUCS2_GetSize(...) { return 0; }
 int PyUnicodeUCS2_GetSize(...) { return 0; }
 int PyUnicodeUCS4_FromFormat(...) { return 0; }
 int PyUnicodeUCS4_FromFormat(...) { return 0; }
 int PyUnicodeUCS4_FromString(...) { return 0; }
 int PyUnicodeUCS4_FromString(...) { return 0; }
 int PyUnicodeUCS4_FromStringAndSize(...) { return 0; }
 int PyUnicodeUCS4_FromStringAndSize(...) { return 0; }
 int PyUnicodeUCS4_FromWideChar(...) { return 0; }
 int PyUnicodeUCS4_FromWideChar(...) { return 0; }
 int PyUnicodeUCS4_AsWideChar(...) { return 0; }
 int PyUnicodeUCS4_AsWideChar(...) { return 0; }
+int PyUnicodeUCS4_AsWideCharString(...) { return 0; }
 int PyUnicodeUCS4_GetSize(...) { return 0; }
 int PyUnicodeUCS4_GetSize(...) { return 0; }
 int PyUnicode_AsUTF8(...) { return 0; }
 int PyUnicode_AsUTF8(...) { return 0; }
 int PyUnicode_AsUTF8AndSize(...) { return 0; }
 int PyUnicode_AsUTF8AndSize(...) { return 0; }

+ 29 - 3
makepanda/installer.nsi

@@ -31,6 +31,7 @@ SetCompressor ${COMPRESSOR}
 !include "Sections.nsh"
 !include "Sections.nsh"
 !include "WinMessages.nsh"
 !include "WinMessages.nsh"
 !include "WordFunc.nsh"
 !include "WordFunc.nsh"
+!include "x64.nsh"
 
 
 !define MUI_WELCOMEFINISHPAGE_BITMAP "panda-install.bmp"
 !define MUI_WELCOMEFINISHPAGE_BITMAP "panda-install.bmp"
 !define MUI_UNWELCOMEFINISHPAGE_BITMAP "panda-install.bmp"
 !define MUI_UNWELCOMEFINISHPAGE_BITMAP "panda-install.bmp"
@@ -120,6 +121,14 @@ Function runFunction
     ExecShell "open" "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk"
     ExecShell "open" "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk"
 FunctionEnd
 FunctionEnd
 
 
+Function .onInit
+    ${If} ${REGVIEW} = 64
+    ${AndIfNot} ${RunningX64}
+        MessageBox MB_OK|MB_ICONEXCLAMATION "You are attempting to install the 64-bit version of Panda3D on a 32-bit version of Windows.  Please download and install the 32-bit version of Panda3D instead."
+        Abort
+    ${EndIf}
+FunctionEnd
+
 SectionGroup "Panda3D Libraries"
 SectionGroup "Panda3D Libraries"
     Section "Core Libraries" SecCore
     Section "Core Libraries" SecCore
         SectionIn 1 2 RO
         SectionIn 1 2 RO
@@ -134,12 +143,22 @@ SectionGroup "Panda3D Libraries"
         SetOutPath "$INSTDIR"
         SetOutPath "$INSTDIR"
         File "${BUILT}\LICENSE"
         File "${BUILT}\LICENSE"
         File /r /x CVS "${BUILT}\ReleaseNotes"
         File /r /x CVS "${BUILT}\ReleaseNotes"
-        SetOutPath $INSTDIR\bin
-        File /r /x libpandagl.dll /x libpandadx9.dll /x cgD3D*.dll /x python*.dll /x libpandaode.dll /x libp3fmod_audio.dll /x fmodex*.dll /x libp3ffmpeg.dll /x av*.dll /x postproc*.dll /x swscale*.dll /x swresample*.dll /x NxCharacter*.dll /x cudart*.dll /x PhysX*.dll /x libpandaphysx.dll /x libp3rocket.dll /x boost_python*.dll /x Rocket*.dll /x _rocket*.pyd /x libpandabullet.dll /x OpenAL32.dll /x *_oal.dll /x libp3openal_audio.dll "${BUILT}\bin\*.dll"
-        File /nonfatal /r "${BUILT}\bin\Microsoft.*.manifest"
+
         SetOutPath $INSTDIR\etc
         SetOutPath $INSTDIR\etc
         File /r "${BUILT}\etc\*"
         File /r "${BUILT}\etc\*"
 
 
+        SetOutPath $INSTDIR\bin
+        File /r /x api-ms-win-*.dll /x ucrtbase.dll /x libpandagl.dll /x libpandadx9.dll /x cgD3D*.dll /x python*.dll /x libpandaode.dll /x libp3fmod_audio.dll /x fmodex*.dll /x libp3ffmpeg.dll /x av*.dll /x postproc*.dll /x swscale*.dll /x swresample*.dll /x NxCharacter*.dll /x cudart*.dll /x PhysX*.dll /x libpandaphysx.dll /x libp3rocket.dll /x boost_python*.dll /x Rocket*.dll /x _rocket*.pyd /x libpandabullet.dll /x OpenAL32.dll /x *_oal.dll /x libp3openal_audio.dll "${BUILT}\bin\*.dll"
+        File /nonfatal /r "${BUILT}\bin\Microsoft.*.manifest"
+
+        ; Before Windows 10, we need these stubs for the UCRT as well.
+        ReadRegDWORD $0 HKLM "Software\Microsoft\Windows NT\CurrentVersion" "CurrentMajorVersionNumber"
+        ${If} $0 < 10
+            ClearErrors
+            File /nonfatal /r "${BUILT}\bin\api-ms-win-*.dll"
+            File /nonfatal "${BUILT}\bin\ucrtbase.dll"
+        ${Endif}
+
         SetDetailsPrint both
         SetDetailsPrint both
         DetailPrint "Installing models..."
         DetailPrint "Installing models..."
         SetDetailsPrint listonly
         SetDetailsPrint listonly
@@ -147,6 +166,10 @@ SectionGroup "Panda3D Libraries"
         SetOutPath $INSTDIR\models
         SetOutPath $INSTDIR\models
         File /r /x CVS "${BUILT}\models\*"
         File /r /x CVS "${BUILT}\models\*"
 
 
+        SetDetailsPrint both
+        DetailPrint "Installing optional components..."
+        SetDetailsPrint listonly
+
         RMDir /r "$SMPROGRAMS\${TITLE}"
         RMDir /r "$SMPROGRAMS\${TITLE}"
         CreateDirectory "$SMPROGRAMS\${TITLE}"
         CreateDirectory "$SMPROGRAMS\${TITLE}"
     SectionEnd
     SectionEnd
@@ -634,6 +657,9 @@ Section -post
     WriteRegStr HKCU "Software\Classes\.pz" "PerceivedType" "compressed"
     WriteRegStr HKCU "Software\Classes\.pz" "PerceivedType" "compressed"
     WriteRegStr HKCU "Software\Classes\.mf" "" "Panda3D.Multifile"
     WriteRegStr HKCU "Software\Classes\.mf" "" "Panda3D.Multifile"
     WriteRegStr HKCU "Software\Classes\.mf" "PerceivedType" "compressed"
     WriteRegStr HKCU "Software\Classes\.mf" "PerceivedType" "compressed"
+    WriteRegStr HKCU "Software\Classes\.prc" "" "inifile"
+    WriteRegStr HKCU "Software\Classes\.prc" "Content Type" "text/plain"
+    WriteRegStr HKCU "Software\Classes\.prc" "PerceivedType" "text"
 
 
     ; For convenience, if nobody registered .pyd, we will.
     ; For convenience, if nobody registered .pyd, we will.
     ReadRegStr $0 HKCR "Software\Classes\.pyd" ""
     ReadRegStr $0 HKCR "Software\Classes\.pyd" ""

+ 169 - 95
makepanda/makepanda.py

@@ -39,6 +39,7 @@ import sys
 
 
 COMPILER=0
 COMPILER=0
 INSTALLER=0
 INSTALLER=0
+WHEEL=0
 GENMAN=0
 GENMAN=0
 COMPRESSOR="zlib"
 COMPRESSOR="zlib"
 THREADCOUNT=0
 THREADCOUNT=0
@@ -87,14 +88,13 @@ PkgListSet(["PYTHON", "DIRECT",                        # Python support
   "MFC", "WX", "FLTK",                                 # Used for web plug-in only
   "MFC", "WX", "FLTK",                                 # Used for web plug-in only
   "ROCKET", "AWESOMIUM",                               # GUI libraries
   "ROCKET", "AWESOMIUM",                               # GUI libraries
   "CARBON", "COCOA",                                   # Mac OS X toolkits
   "CARBON", "COCOA",                                   # Mac OS X toolkits
-  "X11", "XF86DGA", "XRANDR", "XCURSOR",               # Unix platform support
-  "PANDATOOL", "PVIEW", "DEPLOYTOOLS",                 # Toolchain
+  "X11",                                               # Unix platform support
+  "PANDATOOL", "PVIEW", "DEPLOYTOOLS", "DIRECTSCRIPTS",# Toolchain
   "SKEL",                                              # Example SKEL project
   "SKEL",                                              # Example SKEL project
   "PANDAFX",                                           # Some distortion special lenses
   "PANDAFX",                                           # Some distortion special lenses
   "PANDAPARTICLESYSTEM",                               # Built in particle system
   "PANDAPARTICLESYSTEM",                               # Built in particle system
   "CONTRIB",                                           # Experimental
   "CONTRIB",                                           # Experimental
   "SSE2", "NEON",                                      # Compiler features
   "SSE2", "NEON",                                      # Compiler features
-  "TOUCHINPUT",                                        # Touchinput interface (requires Windows 7)
 ])
 ])
 
 
 CheckPandaSourceTree()
 CheckPandaSourceTree()
@@ -125,6 +125,7 @@ def usage(problem):
     print("  --verbose         (print out more information)")
     print("  --verbose         (print out more information)")
     print("  --runtime         (build a runtime build instead of an SDK build)")
     print("  --runtime         (build a runtime build instead of an SDK build)")
     print("  --installer       (build an installer)")
     print("  --installer       (build an installer)")
+    print("  --wheel           (build a pip-installable .whl)")
     print("  --optimize X      (optimization level can be 1,2,3,4)")
     print("  --optimize X      (optimization level can be 1,2,3,4)")
     print("  --version X       (set the panda version number)")
     print("  --version X       (set the panda version number)")
     print("  --lzma            (use lzma compression when building Windows installer)")
     print("  --lzma            (use lzma compression when building Windows installer)")
@@ -160,17 +161,18 @@ def usage(problem):
     os._exit(1)
     os._exit(1)
 
 
 def parseopts(args):
 def parseopts(args):
-    global INSTALLER,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION
+    global INSTALLER,WHEEL,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION
     global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL
     global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL
     global DEBVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION
     global DEBVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION
     global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER
     global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER
     longopts = [
     longopts = [
         "help","distributor=","verbose","runtime","osxtarget=",
         "help","distributor=","verbose","runtime","osxtarget=",
-        "optimize=","everything","nothing","installer","rtdist","nocolor",
+        "optimize=","everything","nothing","installer","wheel","rtdist","nocolor",
         "version=","lzma","no-python","threads=","outputdir=","override=",
         "version=","lzma","no-python","threads=","outputdir=","override=",
         "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=",
         "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=",
         "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl",
         "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl",
-        "universal", "target=", "arch=", "git-commit="]
+        "universal", "target=", "arch=", "git-commit=",
+        "use-touchinput", "no-touchinput"]
     anything = 0
     anything = 0
     optimize = ""
     optimize = ""
     target = None
     target = None
@@ -188,6 +190,7 @@ def parseopts(args):
             if (option=="--help"): raise Exception
             if (option=="--help"): raise Exception
             elif (option=="--optimize"): optimize=value
             elif (option=="--optimize"): optimize=value
             elif (option=="--installer"): INSTALLER=1
             elif (option=="--installer"): INSTALLER=1
+            elif (option=="--wheel"): WHEEL=1
             elif (option=="--verbose"): SetVerbose(True)
             elif (option=="--verbose"): SetVerbose(True)
             elif (option=="--distributor"): DISTRIBUTOR=value
             elif (option=="--distributor"): DISTRIBUTOR=value
             elif (option=="--rtdist"): RTDIST=1
             elif (option=="--rtdist"): RTDIST=1
@@ -316,18 +319,6 @@ def parseopts(args):
             print("No Windows SDK version specified. Defaulting to '7.1'.")
             print("No Windows SDK version specified. Defaulting to '7.1'.")
             WINDOWS_SDK = '7.1'
             WINDOWS_SDK = '7.1'
 
 
-        is_win7 = False
-        if sys.platform == 'win32':
-            # Note: not available in cygwin.
-            winver = sys.getwindowsversion()
-            if winver[0] >= 6 and winver[1] >= 1:
-                is_win7 = True
-
-        if RUNTIME or not is_win7:
-            PkgDisable("TOUCHINPUT")
-    else:
-        PkgDisable("TOUCHINPUT")
-
     if clean_build and os.path.isdir(GetOutputDir()):
     if clean_build and os.path.isdir(GetOutputDir()):
         print("Deleting %s" % (GetOutputDir()))
         print("Deleting %s" % (GetOutputDir()))
         shutil.rmtree(GetOutputDir())
         shutil.rmtree(GetOutputDir())
@@ -428,9 +419,18 @@ if (RUNTIME):
 if (INSTALLER and RTDIST):
 if (INSTALLER and RTDIST):
     exit("Cannot build an installer for the rtdist build!")
     exit("Cannot build an installer for the rtdist build!")
 
 
+if (WHEEL and RUNTIME):
+    exit("Cannot build a wheel for the runtime build!")
+
+if (WHEEL and RTDIST):
+    exit("Cannot build a wheel for the rtdist build!")
+
 if (INSTALLER) and (PkgSkip("PYTHON")) and (not RUNTIME) and GetTarget() == 'windows':
 if (INSTALLER) and (PkgSkip("PYTHON")) and (not RUNTIME) and GetTarget() == 'windows':
     exit("Cannot build installer on Windows without python")
     exit("Cannot build installer on Windows without python")
 
 
+if WHEEL and PkgSkip("PYTHON"):
+    exit("Cannot build wheel without Python")
+
 if (RTDIST) and (PkgSkip("WX") and PkgSkip("FLTK")):
 if (RTDIST) and (PkgSkip("WX") and PkgSkip("FLTK")):
     exit("Cannot build rtdist without wx or fltk")
     exit("Cannot build rtdist without wx or fltk")
 
 
@@ -516,9 +516,6 @@ IncDirectory("ALWAYS", GetOutputDir()+"/include")
 
 
 if (COMPILER == "MSVC"):
 if (COMPILER == "MSVC"):
     PkgDisable("X11")
     PkgDisable("X11")
-    PkgDisable("XRANDR")
-    PkgDisable("XF86DGA")
-    PkgDisable("XCURSOR")
     PkgDisable("GLES")
     PkgDisable("GLES")
     PkgDisable("GLES2")
     PkgDisable("GLES2")
     PkgDisable("EGL")
     PkgDisable("EGL")
@@ -809,9 +806,13 @@ if (COMPILER=="GCC"):
         SmartPkgEnable("JPEG",      "",          ("jpeg"), "jpeglib.h")
         SmartPkgEnable("JPEG",      "",          ("jpeg"), "jpeglib.h")
         SmartPkgEnable("PNG",       "libpng",    ("png"), "png.h", tool = "libpng-config")
         SmartPkgEnable("PNG",       "libpng",    ("png"), "png.h", tool = "libpng-config")
 
 
-        if GetTarget() == "darwin" and not PkgSkip("FFMPEG"):
-            LibName("FFMPEG", "-Wl,-read_only_relocs,suppress")
-            LibName("FFMPEG", "-framework VideoDecodeAcceleration")
+        if not PkgSkip("FFMPEG"):
+            if GetTarget() == "darwin":
+                LibName("FFMPEG", "-Wl,-read_only_relocs,suppress")
+                LibName("FFMPEG", "-framework VideoDecodeAcceleration")
+            elif os.path.isfile(GetThirdpartyDir() + "ffmpeg/lib/libavcodec.a"):
+                # Needed when linking ffmpeg statically on Linux.
+                LibName("FFMPEG", "-Wl,-Bsymbolic")
 
 
         cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV")
         cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV")
         if cv_lib == "opencv_core":
         if cv_lib == "opencv_core":
@@ -841,12 +842,9 @@ if (COMPILER=="GCC"):
     if GetTarget() != 'darwin':
     if GetTarget() != 'darwin':
         # CgGL is covered by the Cg framework, and we don't need X11 components on OSX
         # CgGL is covered by the Cg framework, and we don't need X11 components on OSX
         if not PkgSkip("NVIDIACG") and not RUNTIME:
         if not PkgSkip("NVIDIACG") and not RUNTIME:
-            SmartPkgEnable("CGGL", "", ("CgGL"), "Cg/cgGL.h")
+            SmartPkgEnable("CGGL", "", ("CgGL"), "Cg/cgGL.h", thirdparty_dir = "nvidiacg")
         if not RUNTIME:
         if not RUNTIME:
             SmartPkgEnable("X11", "x11", "X11", ("X11", "X11/Xlib.h"))
             SmartPkgEnable("X11", "x11", "X11", ("X11", "X11/Xlib.h"))
-            SmartPkgEnable("XRANDR", "xrandr", "Xrandr", "X11/extensions/Xrandr.h")
-            SmartPkgEnable("XF86DGA", "xxf86dga", "Xxf86dga", "X11/extensions/xf86dga.h")
-            SmartPkgEnable("XCURSOR", "xcursor", "Xcursor", "X11/Xcursor/Xcursor.h")
 
 
     if GetHost() != "darwin":
     if GetHost() != "darwin":
         # Workaround for an issue where pkg-config does not include this path
         # Workaround for an issue where pkg-config does not include this path
@@ -1056,11 +1054,12 @@ def CompileCxx(obj,src,opts):
                 cmd += "/favor:blend "
                 cmd += "/favor:blend "
             cmd += "/wd4996 /wd4275 /wd4273 "
             cmd += "/wd4996 /wd4275 /wd4273 "
 
 
-            # Enable Windows 7 interfaces if we need Touchinput.
-            if PkgSkip("TOUCHINPUT") == 0:
-                cmd += "/DWINVER=0x601 "
-            else:
-                cmd += "/DWINVER=0x501 "
+            # We still target Windows XP.
+            cmd += "/DWINVER=0x501 "
+            # Work around a WinXP/2003 bug when using VS 2015+.
+            if SDK.get("VISUALSTUDIO_VERSION") == '14.0':
+                cmd += "/Zc:threadSafeInit- "
+
             cmd += "/Fo" + obj + " /nologo /c"
             cmd += "/Fo" + obj + " /nologo /c"
             if GetTargetArch() != 'x64' and (not PkgSkip("SSE2") or 'SSE2' in opts):
             if GetTargetArch() != 'x64' and (not PkgSkip("SSE2") or 'SSE2' in opts):
                 cmd += " /arch:SSE2"
                 cmd += " /arch:SSE2"
@@ -1110,12 +1109,7 @@ def CompileCxx(obj,src,opts):
             if GetTargetArch() == 'x64':
             if GetTargetArch() == 'x64':
                 cmd += "/favor:blend "
                 cmd += "/favor:blend "
             cmd += "/wd4996 /wd4275 /wd4267 /wd4101 /wd4273 "
             cmd += "/wd4996 /wd4275 /wd4267 /wd4101 /wd4273 "
-
-            # Enable Windows 7 interfaces if we need Touchinput.
-            if PkgSkip("TOUCHINPUT") == 0:
-                cmd += "/DWINVER=0x601 "
-            else:
-                cmd += "/DWINVER=0x501 "
+            cmd += "/DWINVER=0x501 "
             cmd += "/Fo" + obj + " /c"
             cmd += "/Fo" + obj + " /c"
             for x in ipath: cmd += " /I" + x
             for x in ipath: cmd += " /I" + x
             for (opt,dir) in INCDIRECTORIES:
             for (opt,dir) in INCDIRECTORIES:
@@ -1683,11 +1677,7 @@ def CompileLink(dll, obj, opts):
                 if 'NOARCH:' + arch.upper() not in opts:
                 if 'NOARCH:' + arch.upper() not in opts:
                     cmd += " -arch %s" % arch
                     cmd += " -arch %s" % arch
 
 
-        if "SYSROOT" in SDK:
-            cmd += " --sysroot=%s -no-canonical-prefixes" % (SDK["SYSROOT"])
-
-        # Android-specific flags.
-        if GetTarget() == 'android':
+        elif GetTarget() == 'android':
             cmd += " -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now"
             cmd += " -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now"
             if GetTargetArch() == 'armv7a':
             if GetTargetArch() == 'armv7a':
                 cmd += " -march=armv7-a -Wl,--fix-cortex-a8"
                 cmd += " -march=armv7-a -Wl,--fix-cortex-a8"
@@ -1695,9 +1685,17 @@ def CompileLink(dll, obj, opts):
         else:
         else:
             cmd += " -pthread"
             cmd += " -pthread"
 
 
+        if "SYSROOT" in SDK:
+            cmd += " --sysroot=%s -no-canonical-prefixes" % (SDK["SYSROOT"])
+
         if LDFLAGS != "":
         if LDFLAGS != "":
             cmd += " " + LDFLAGS
             cmd += " " + LDFLAGS
 
 
+        # Don't link libraries with Python.
+        if "PYTHON" in opts and GetOrigExt(dll) != ".exe" and not RTDIST:
+            opts = opts[:]
+            opts.remove("PYTHON")
+
         for (opt, dir) in LIBDIRECTORIES:
         for (opt, dir) in LIBDIRECTORIES:
             if (opt=="ALWAYS") or (opt in opts):
             if (opt=="ALWAYS") or (opt in opts):
                 cmd += ' -L' + BracketNameWithQuotes(dir)
                 cmd += ' -L' + BracketNameWithQuotes(dir)
@@ -2127,7 +2125,6 @@ DTOOL_CONFIG=[
     ("REPORT_OPENSSL_ERRORS",          '1',                      '1'),
     ("REPORT_OPENSSL_ERRORS",          '1',                      '1'),
     ("USE_PANDAFILESTREAM",            '1',                      '1'),
     ("USE_PANDAFILESTREAM",            '1',                      '1'),
     ("USE_DELETED_CHAIN",              '1',                      '1'),
     ("USE_DELETED_CHAIN",              '1',                      '1'),
-    ("HAVE_WIN_TOUCHINPUT",            'UNDEF',                  'UNDEF'),
     ("HAVE_GLX",                       'UNDEF',                  '1'),
     ("HAVE_GLX",                       'UNDEF',                  '1'),
     ("HAVE_WGL",                       '1',                      'UNDEF'),
     ("HAVE_WGL",                       '1',                      'UNDEF'),
     ("HAVE_DX9",                       'UNDEF',                  'UNDEF'),
     ("HAVE_DX9",                       'UNDEF',                  'UNDEF'),
@@ -2201,9 +2198,6 @@ DTOOL_CONFIG=[
     ("PHAVE_STDINT_H",                 '1',                      '1'),
     ("PHAVE_STDINT_H",                 '1',                      '1'),
     ("HAVE_RTTI",                      '1',                      '1'),
     ("HAVE_RTTI",                      '1',                      '1'),
     ("HAVE_X11",                       'UNDEF',                  '1'),
     ("HAVE_X11",                       'UNDEF',                  '1'),
-    ("HAVE_XRANDR",                    'UNDEF',                  '1'),
-    ("HAVE_XF86DGA",                   'UNDEF',                  '1'),
-    ("HAVE_XCURSOR",                   'UNDEF',                  '1'),
     ("IS_LINUX",                       'UNDEF',                  '1'),
     ("IS_LINUX",                       'UNDEF',                  '1'),
     ("IS_OSX",                         'UNDEF',                  'UNDEF'),
     ("IS_OSX",                         'UNDEF',                  'UNDEF'),
     ("IS_FREEBSD",                     'UNDEF',                  'UNDEF'),
     ("IS_FREEBSD",                     'UNDEF',                  'UNDEF'),
@@ -2234,11 +2228,7 @@ DTOOL_CONFIG=[
     ("HAVE_CG",                        'UNDEF',                  'UNDEF'),
     ("HAVE_CG",                        'UNDEF',                  'UNDEF'),
     ("HAVE_CGGL",                      'UNDEF',                  'UNDEF'),
     ("HAVE_CGGL",                      'UNDEF',                  'UNDEF'),
     ("HAVE_CGDX9",                     'UNDEF',                  'UNDEF'),
     ("HAVE_CGDX9",                     'UNDEF',                  'UNDEF'),
-    ("HAVE_FFMPEG",                    'UNDEF',                  'UNDEF'),
-    ("HAVE_SWSCALE",                   'UNDEF',                  'UNDEF'),
-    ("HAVE_SWRESAMPLE",                'UNDEF',                  'UNDEF'),
     ("HAVE_ARTOOLKIT",                 'UNDEF',                  'UNDEF'),
     ("HAVE_ARTOOLKIT",                 'UNDEF',                  'UNDEF'),
-    ("HAVE_OPENCV",                    'UNDEF',                  'UNDEF'),
     ("HAVE_DIRECTCAM",                 'UNDEF',                  'UNDEF'),
     ("HAVE_DIRECTCAM",                 'UNDEF',                  'UNDEF'),
     ("HAVE_SQUISH",                    'UNDEF',                  'UNDEF'),
     ("HAVE_SQUISH",                    'UNDEF',                  'UNDEF'),
     ("HAVE_CARBON",                    'UNDEF',                  'UNDEF'),
     ("HAVE_CARBON",                    'UNDEF',                  'UNDEF'),
@@ -2293,9 +2283,6 @@ def WriteConfigSettings():
             else:
             else:
                 dtool_config["HAVE_"+x] = 'UNDEF'
                 dtool_config["HAVE_"+x] = 'UNDEF'
 
 
-    if not PkgSkip("OPENCV"):
-        dtool_config["OPENCV_VER_23"] = '1' if OPENCV_VER_23 else 'UNDEF'
-
     dtool_config["HAVE_NET"] = '1'
     dtool_config["HAVE_NET"] = '1'
 
 
     if (PkgSkip("NVIDIACG")==0):
     if (PkgSkip("NVIDIACG")==0):
@@ -2315,9 +2302,6 @@ def WriteConfigSettings():
         dtool_config["PHAVE_SYS_MALLOC_H"] = '1'
         dtool_config["PHAVE_SYS_MALLOC_H"] = '1'
         dtool_config["HAVE_OPENAL_FRAMEWORK"] = '1'
         dtool_config["HAVE_OPENAL_FRAMEWORK"] = '1'
         dtool_config["HAVE_X11"] = 'UNDEF'  # We might have X11, but we don't need it.
         dtool_config["HAVE_X11"] = 'UNDEF'  # We might have X11, but we don't need it.
-        dtool_config["HAVE_XRANDR"] = 'UNDEF'
-        dtool_config["HAVE_XF86DGA"] = 'UNDEF'
-        dtool_config["HAVE_XCURSOR"] = 'UNDEF'
         dtool_config["HAVE_GLX"] = 'UNDEF'
         dtool_config["HAVE_GLX"] = 'UNDEF'
         dtool_config["IS_LINUX"] = 'UNDEF'
         dtool_config["IS_LINUX"] = 'UNDEF'
         dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF'
         dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF'
@@ -2352,9 +2336,6 @@ def WriteConfigSettings():
     if (PkgSkip("PYTHON") != 0):
     if (PkgSkip("PYTHON") != 0):
         dtool_config["HAVE_ROCKET_PYTHON"] = 'UNDEF'
         dtool_config["HAVE_ROCKET_PYTHON"] = 'UNDEF'
 
 
-    if (PkgSkip("TOUCHINPUT") == 0 and GetTarget() == "windows"):
-        dtool_config["HAVE_WIN_TOUCHINPUT"] = '1'
-
     if (GetOptimize() <= 3):
     if (GetOptimize() <= 3):
         dtool_config["HAVE_ROCKET_DEBUGGER"] = '1'
         dtool_config["HAVE_ROCKET_DEBUGGER"] = '1'
 
 
@@ -2782,8 +2763,13 @@ if (GetTarget() == 'darwin'):
     # OpenAL is not yet working well on OSX for us, so let's do this for now.
     # OpenAL is not yet working well on OSX for us, so let's do this for now.
     configprc = configprc.replace("p3openal_audio", "p3fmod_audio")
     configprc = configprc.replace("p3openal_audio", "p3fmod_audio")
 
 
-ConditionalWriteFile(GetOutputDir()+"/etc/Config.prc", configprc)
-ConditionalWriteFile(GetOutputDir()+"/etc/Confauto.prc", confautoprc)
+if GetTarget() == 'windows':
+    # Convert to Windows newlines.
+    ConditionalWriteFile(GetOutputDir()+"/etc/Config.prc", configprc, newline='\r\n')
+    ConditionalWriteFile(GetOutputDir()+"/etc/Confauto.prc", confautoprc, newline='\r\n')
+else:
+    ConditionalWriteFile(GetOutputDir()+"/etc/Config.prc", configprc)
+    ConditionalWriteFile(GetOutputDir()+"/etc/Confauto.prc", confautoprc)
 
 
 ##########################################################################################
 ##########################################################################################
 #
 #
@@ -2880,22 +2866,75 @@ if tp_dir is not None:
 
 
     if GetTarget() == 'windows':
     if GetTarget() == 'windows':
         CopyAllFiles(GetOutputDir() + "/bin/", tp_dir + "extras/bin/")
         CopyAllFiles(GetOutputDir() + "/bin/", tp_dir + "extras/bin/")
-        if not PkgSkip("PYTHON"):
-            pydll = "/" + SDK["PYTHONVERSION"].replace(".", "")
-            if (GetOptimize() <= 2): pydll += "_d.dll"
-            else: pydll += ".dll"
-            CopyFile(GetOutputDir() + "/bin" + pydll, SDK["PYTHON"] + pydll)
-
-            for fn in glob.glob(SDK["PYTHON"] + "/vcruntime*.dll"):
-                CopyFile(GetOutputDir() + "/bin/", fn)
-
-            if not RTDIST:
-                CopyTree(GetOutputDir() + "/python", SDK["PYTHON"])
-                if not os.path.isfile(SDK["PYTHON"] + "/ppython.exe") and os.path.isfile(SDK["PYTHON"] + "/python.exe"):
-                    CopyFile(GetOutputDir() + "/python/ppython.exe", SDK["PYTHON"] + "/python.exe")
-                if not os.path.isfile(SDK["PYTHON"] + "/ppythonw.exe") and os.path.isfile(SDK["PYTHON"] + "/pythonw.exe"):
-                    CopyFile(GetOutputDir() + "/python/ppythonw.exe", SDK["PYTHON"] + "/pythonw.exe")
-                ConditionalWriteFile(GetOutputDir() + "/python/panda.pth", "..\n../bin\n")
+
+        if not PkgSkip("PYTHON") and not RTDIST:
+            #XXX rdb I don't think we need to copy over the Python DLL, do we?
+            #pydll = "/" + SDK["PYTHONVERSION"].replace(".", "")
+            #if (GetOptimize() <= 2): pydll += "_d.dll"
+            #else: pydll += ".dll"
+            #CopyFile(GetOutputDir() + "/bin" + pydll, SDK["PYTHON"] + pydll)
+
+            #for fn in glob.glob(SDK["PYTHON"] + "/vcruntime*.dll"):
+            #    CopyFile(GetOutputDir() + "/bin/", fn)
+
+            # Copy the whole Python directory.
+            CopyTree(GetOutputDir() + "/python", SDK["PYTHON"])
+
+            # NB: Python does not always ship with the correct manifest/dll.
+            # Figure out the correct one to ship, and grab it from WinSxS dir.
+            manifest = GetOutputDir() + '/tmp/python.manifest'
+            if os.path.isfile(manifest):
+                os.unlink(manifest)
+            oscmd('mt -inputresource:"%s\\python.exe";#1 -out:"%s" -nologo' % (SDK["PYTHON"], manifest), True)
+
+            if os.path.isfile(manifest):
+                import xml.etree.ElementTree as ET
+                tree = ET.parse(manifest)
+                idents = tree.findall('./{urn:schemas-microsoft-com:asm.v1}dependency/{urn:schemas-microsoft-com:asm.v1}dependentAssembly/{urn:schemas-microsoft-com:asm.v1}assemblyIdentity')
+            else:
+                idents = ()
+
+            for ident in tree.findall('./{urn:schemas-microsoft-com:asm.v1}dependency/{urn:schemas-microsoft-com:asm.v1}dependentAssembly/{urn:schemas-microsoft-com:asm.v1}assemblyIdentity'):
+                sxs_name = '_'.join([
+                    ident.get('processorArchitecture'),
+                    ident.get('name').lower(),
+                    ident.get('publicKeyToken'),
+                    ident.get('version'),
+                ])
+
+                # Find the manifest matching these parameters.
+                pattern = os.path.join('C:' + os.sep, 'Windows', 'WinSxS', 'Manifests', sxs_name + '_*.manifest')
+                manifests = glob.glob(pattern)
+                if not manifests:
+                    print("%sWARNING:%s Could not locate manifest %s.  You may need to reinstall the Visual C++ Redistributable." % (GetColor("red"), GetColor(), pattern))
+                    continue
+
+                CopyFile(GetOutputDir() + "/python/" + ident.get('name') + ".manifest", manifests[0])
+
+                # Also copy the corresponding msvcr dll.
+                pattern = os.path.join('C:' + os.sep, 'Windows', 'WinSxS', sxs_name + '_*', 'msvcr*.dll')
+                for file in glob.glob(pattern):
+                    CopyFile(GetOutputDir() + "/python/", file)
+
+            # Copy python.exe to ppython.exe.
+            if not os.path.isfile(SDK["PYTHON"] + "/ppython.exe") and os.path.isfile(SDK["PYTHON"] + "/python.exe"):
+                CopyFile(GetOutputDir() + "/python/ppython.exe", SDK["PYTHON"] + "/python.exe")
+            if not os.path.isfile(SDK["PYTHON"] + "/ppythonw.exe") and os.path.isfile(SDK["PYTHON"] + "/pythonw.exe"):
+                CopyFile(GetOutputDir() + "/python/ppythonw.exe", SDK["PYTHON"] + "/pythonw.exe")
+            ConditionalWriteFile(GetOutputDir() + "/python/panda.pth", "..\n../bin\n")
+
+# Copy over the MSVC runtime.
+if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK:
+    vcver = SDK["VISUALSTUDIO_VERSION"].replace('.', '')
+    crtname = "Microsoft.VC%s.CRT" % (vcver)
+    dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "redist", GetTargetArch(), crtname)
+
+    if os.path.isfile(os.path.join(dir, "msvcr" + vcver + ".dll")):
+        CopyFile(GetOutputDir() + "/bin/", os.path.join(dir, "msvcr" + vcver + ".dll"))
+    if os.path.isfile(os.path.join(dir, "msvcp" + vcver + ".dll")):
+        CopyFile(GetOutputDir() + "/bin/", os.path.join(dir, "msvcp" + vcver + ".dll"))
+    if os.path.isfile(os.path.join(dir, "vcruntime" + vcver + ".dll")):
+        CopyFile(GetOutputDir() + "/bin/", os.path.join(dir, "vcruntime" + vcver + ".dll"))
 
 
 ########################################################################
 ########################################################################
 ##
 ##
@@ -2903,8 +2942,14 @@ if tp_dir is not None:
 ##
 ##
 ########################################################################
 ########################################################################
 
 
-CopyFile(GetOutputDir()+"/", "doc/LICENSE")
-CopyFile(GetOutputDir()+"/", "doc/ReleaseNotes")
+if GetTarget() == 'windows':
+    # Convert to Windows newlines so they can be opened by notepad.
+    WriteFile(GetOutputDir() + "/LICENSE", ReadFile("doc/LICENSE"), newline='\r\n')
+    WriteFile(GetOutputDir() + "/ReleaseNotes", ReadFile("doc/ReleaseNotes"), newline='\r\n')
+else:
+    CopyFile(GetOutputDir()+"/", "doc/LICENSE")
+    CopyFile(GetOutputDir()+"/", "doc/ReleaseNotes")
+
 if (PkgSkip("PANDATOOL")==0):
 if (PkgSkip("PANDATOOL")==0):
     CopyAllFiles(GetOutputDir()+"/plugins/",  "pandatool/src/scripts/", ".mel")
     CopyAllFiles(GetOutputDir()+"/plugins/",  "pandatool/src/scripts/", ".mel")
     CopyAllFiles(GetOutputDir()+"/plugins/",  "pandatool/src/scripts/", ".ms")
     CopyAllFiles(GetOutputDir()+"/plugins/",  "pandatool/src/scripts/", ".ms")
@@ -3293,6 +3338,7 @@ if (not RUNTIME):
   TargetAdd('interrogate.exe', input='libp3cppParser.ilb')
   TargetAdd('interrogate.exe', input='libp3cppParser.ilb')
   TargetAdd('interrogate.exe', input=COMMON_DTOOL_LIBS)
   TargetAdd('interrogate.exe', input=COMMON_DTOOL_LIBS)
   TargetAdd('interrogate.exe', input='libp3interrogatedb.dll')
   TargetAdd('interrogate.exe', input='libp3interrogatedb.dll')
+  TargetAdd('interrogate.exe', input='libp3pystub.lib')
   TargetAdd('interrogate.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
   TargetAdd('interrogate.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
 
 
   TargetAdd('interrogate_module_interrogate_module.obj', opts=OPTS, input='interrogate_module.cxx')
   TargetAdd('interrogate_module_interrogate_module.obj', opts=OPTS, input='interrogate_module.cxx')
@@ -3300,6 +3346,7 @@ if (not RUNTIME):
   TargetAdd('interrogate_module.exe', input='libp3cppParser.ilb')
   TargetAdd('interrogate_module.exe', input='libp3cppParser.ilb')
   TargetAdd('interrogate_module.exe', input=COMMON_DTOOL_LIBS)
   TargetAdd('interrogate_module.exe', input=COMMON_DTOOL_LIBS)
   TargetAdd('interrogate_module.exe', input='libp3interrogatedb.dll')
   TargetAdd('interrogate_module.exe', input='libp3interrogatedb.dll')
+  TargetAdd('interrogate_module.exe', input='libp3pystub.lib')
   TargetAdd('interrogate_module.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
   TargetAdd('interrogate_module.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
 
 
   if (not RTDIST):
   if (not RTDIST):
@@ -3308,6 +3355,7 @@ if (not RUNTIME):
     TargetAdd('parse_file.exe', input='libp3cppParser.ilb')
     TargetAdd('parse_file.exe', input='libp3cppParser.ilb')
     TargetAdd('parse_file.exe', input=COMMON_DTOOL_LIBS)
     TargetAdd('parse_file.exe', input=COMMON_DTOOL_LIBS)
     TargetAdd('parse_file.exe', input='libp3interrogatedb.dll')
     TargetAdd('parse_file.exe', input='libp3interrogatedb.dll')
+    TargetAdd('parse_file.exe', input='libp3pystub.lib')
     TargetAdd('parse_file.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
     TargetAdd('parse_file.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
 
 
 #
 #
@@ -3331,6 +3379,7 @@ if (not RTDIST and not RUNTIME):
   TargetAdd('test_interrogate.exe', input='test_interrogate_test_interrogate.obj')
   TargetAdd('test_interrogate.exe', input='test_interrogate_test_interrogate.obj')
   TargetAdd('test_interrogate.exe', input='libp3interrogatedb.dll')
   TargetAdd('test_interrogate.exe', input='libp3interrogatedb.dll')
   TargetAdd('test_interrogate.exe', input=COMMON_DTOOL_LIBS)
   TargetAdd('test_interrogate.exe', input=COMMON_DTOOL_LIBS)
+  TargetAdd('test_interrogate.exe', input='libp3pystub.lib')
   TargetAdd('test_interrogate.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
   TargetAdd('test_interrogate.exe', opts=['ADVAPI',  'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER'])
 
 
 #
 #
@@ -4062,8 +4111,20 @@ if (not RUNTIME):
 #
 #
 
 
 if (PkgSkip("VISION") == 0) and (not RUNTIME):
 if (PkgSkip("VISION") == 0) and (not RUNTIME):
+  # We want to know whether we have ffmpeg so that we can override the .avi association.
+  if not PkgSkip("FFMPEG"):
+    DefSymbol("OPENCV", "HAVE_FFMPEG")
+  if not PkgSkip("OPENCV"):
+    DefSymbol("OPENCV", "HAVE_OPENCV")
+    if OPENCV_VER_23:
+        DefSymbol("OPENCV", "OPENCV_VER_23")
+
   OPTS=['DIR:panda/src/vision', 'BUILDING:VISION', 'ARTOOLKIT', 'OPENCV', 'DX9', 'DIRECTCAM', 'JPEG', 'EXCEPTIONS']
   OPTS=['DIR:panda/src/vision', 'BUILDING:VISION', 'ARTOOLKIT', 'OPENCV', 'DX9', 'DIRECTCAM', 'JPEG', 'EXCEPTIONS']
-  TargetAdd('p3vision_composite1.obj', opts=OPTS, input='p3vision_composite1.cxx')
+  TargetAdd('p3vision_composite1.obj', opts=OPTS, input='p3vision_composite1.cxx', dep=[
+    'dtool_have_ffmpeg.dat',
+    'dtool_have_opencv.dat',
+    'dtool_have_directcam.dat',
+  ])
 
 
   TargetAdd('libp3vision.dll', input='p3vision_composite1.obj')
   TargetAdd('libp3vision.dll', input='p3vision_composite1.obj')
   TargetAdd('libp3vision.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libp3vision.dll', input=COMMON_PANDA_LIBS)
@@ -4252,8 +4313,15 @@ if (PkgSkip("VRPN")==0 and not RUNTIME):
 # DIRECTORY: panda/src/ffmpeg
 # DIRECTORY: panda/src/ffmpeg
 #
 #
 if PkgSkip("FFMPEG") == 0 and not RUNTIME:
 if PkgSkip("FFMPEG") == 0 and not RUNTIME:
+  if not PkgSkip("SWSCALE"):
+    DefSymbol("FFMPEG", "HAVE_SWSCALE")
+  if not PkgSkip("SWRESAMPLE"):
+    DefSymbol("FFMPEG", "HAVE_SWRESAMPLE")
+
   OPTS=['DIR:panda/src/ffmpeg', 'BUILDING:FFMPEG', 'FFMPEG', 'SWSCALE', 'SWRESAMPLE']
   OPTS=['DIR:panda/src/ffmpeg', 'BUILDING:FFMPEG', 'FFMPEG', 'SWSCALE', 'SWRESAMPLE']
-  TargetAdd('p3ffmpeg_composite1.obj', opts=OPTS, input='p3ffmpeg_composite1.cxx')
+  TargetAdd('p3ffmpeg_composite1.obj', opts=OPTS, input='p3ffmpeg_composite1.cxx', dep=[
+    'dtool_have_swscale.dat', 'dtool_have_swresample.dat'])
+
   TargetAdd('libp3ffmpeg.dll', input='p3ffmpeg_composite1.obj')
   TargetAdd('libp3ffmpeg.dll', input='p3ffmpeg_composite1.obj')
   TargetAdd('libp3ffmpeg.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libp3ffmpeg.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libp3ffmpeg.dll', opts=OPTS)
   TargetAdd('libp3ffmpeg.dll', opts=OPTS)
@@ -4515,7 +4583,7 @@ if (GetTarget() not in ['windows', 'darwin'] and PkgSkip("GL")==0 and PkgSkip("X
   TargetAdd('libpandagl.dll', input='p3glgsg_glgsg.obj')
   TargetAdd('libpandagl.dll', input='p3glgsg_glgsg.obj')
   TargetAdd('libpandagl.dll', input='p3glxdisplay_composite1.obj')
   TargetAdd('libpandagl.dll', input='p3glxdisplay_composite1.obj')
   TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'X11', 'XRANDR', 'XF86DGA', 'XCURSOR'])
+  TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'X11'])
 
 
 #
 #
 # DIRECTORY: panda/src/cocoadisplay/
 # DIRECTORY: panda/src/cocoadisplay/
@@ -4533,7 +4601,7 @@ if (GetTarget() == 'darwin' and PkgSkip("COCOA")==0 and PkgSkip("GL")==0 and not
   if (PkgSkip('PANDAFX')==0):
   if (PkgSkip('PANDAFX')==0):
     TargetAdd('libpandagl.dll', input='libpandafx.dll')
     TargetAdd('libpandagl.dll', input='libpandafx.dll')
   TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'COCOA'])
+  TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'COCOA', 'CARBON'])
 
 
 #
 #
 # DIRECTORY: panda/src/osxdisplay/
 # DIRECTORY: panda/src/osxdisplay/
@@ -4590,7 +4658,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and PkgSkip("X11")==0 and not RUNTI
   TargetAdd('libpandagles.dll', input='p3glesgsg_glesgsg.obj')
   TargetAdd('libpandagles.dll', input='p3glesgsg_glesgsg.obj')
   TargetAdd('libpandagles.dll', input='pandagles_egldisplay_composite1.obj')
   TargetAdd('libpandagles.dll', input='pandagles_egldisplay_composite1.obj')
   TargetAdd('libpandagles.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libpandagles.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandagles.dll', opts=['MODULE', 'GLES', 'EGL', 'X11', 'XRANDR', 'XF86DGA', 'XCURSOR'])
+  TargetAdd('libpandagles.dll', opts=['MODULE', 'GLES', 'EGL', 'X11'])
 
 
 #
 #
 # DIRECTORY: panda/src/egldisplay/
 # DIRECTORY: panda/src/egldisplay/
@@ -4608,7 +4676,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES2")==0 and PkgSkip("X11")==0 and not RUNT
   TargetAdd('libpandagles2.dll', input='p3gles2gsg_gles2gsg.obj')
   TargetAdd('libpandagles2.dll', input='p3gles2gsg_gles2gsg.obj')
   TargetAdd('libpandagles2.dll', input='pandagles2_egldisplay_composite1.obj')
   TargetAdd('libpandagles2.dll', input='pandagles2_egldisplay_composite1.obj')
   TargetAdd('libpandagles2.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libpandagles2.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandagles2.dll', opts=['MODULE', 'GLES2', 'EGL', 'X11', 'XRANDR', 'XF86DGA', 'XCURSOR'])
+  TargetAdd('libpandagles2.dll', opts=['MODULE', 'GLES2', 'EGL', 'X11'])
 
 
 #
 #
 # DIRECTORY: panda/src/ode/
 # DIRECTORY: panda/src/ode/
@@ -4904,7 +4972,7 @@ if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0)
     TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM'])
     TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM'])
   else:
   else:
     TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj')
     TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj')
-    TargetAdd('libp3tinydisplay.dll', opts=['X11', 'XRANDR', 'XF86DGA', 'XCURSOR'])
+    TargetAdd('libp3tinydisplay.dll', opts=['X11'])
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj')
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj')
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite2.obj')
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite2.obj')
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_ztriangle_1.obj')
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_ztriangle_1.obj')
@@ -4922,7 +4990,7 @@ if (PkgSkip("DIRECT")==0):
   OPTS=['DIR:direct/src/directbase', 'PYTHON']
   OPTS=['DIR:direct/src/directbase', 'PYTHON']
   TargetAdd('p3directbase_directbase.obj', opts=OPTS+['BUILDING:DIRECT'], input='directbase.cxx')
   TargetAdd('p3directbase_directbase.obj', opts=OPTS+['BUILDING:DIRECT'], input='directbase.cxx')
 
 
-  if (PkgSkip("PYTHON")==0 and not RTDIST and not RUNTIME):
+  if not PkgSkip("PYTHON") and not RTDIST and not RUNTIME and not PkgSkip("DIRECTSCRIPTS"):
     DefSymbol("BUILDING:PACKPANDA", "IMPORT_MODULE", "direct.directscripts.packpanda")
     DefSymbol("BUILDING:PACKPANDA", "IMPORT_MODULE", "direct.directscripts.packpanda")
     TargetAdd('packpanda.obj', opts=OPTS+['BUILDING:PACKPANDA'], input='ppython.cxx')
     TargetAdd('packpanda.obj', opts=OPTS+['BUILDING:PACKPANDA'], input='ppython.cxx')
     TargetAdd('packpanda.exe', input='packpanda.obj')
     TargetAdd('packpanda.exe', input='packpanda.obj')
@@ -5097,6 +5165,7 @@ if (PkgSkip("PYTHON")==0 and PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME
   TargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj')
   TargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj')
   TargetAdd('p3dcparse.exe', input='libp3direct.dll')
   TargetAdd('p3dcparse.exe', input='libp3direct.dll')
   TargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS)
   TargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS)
+  TargetAdd('p3dcparse.exe', input='libp3pystub.lib')
   TargetAdd('p3dcparse.exe', opts=['ADVAPI', 'PYTHON'])
   TargetAdd('p3dcparse.exe', opts=['ADVAPI', 'PYTHON'])
 
 
 #
 #
@@ -6780,9 +6849,9 @@ def MakeInstallerLinux():
         txt = txt.replace("VERSION", DEBVERSION).replace("ARCH", pkg_arch).replace("PV", PV).replace("MAJOR", MAJOR_VERSION)
         txt = txt.replace("VERSION", DEBVERSION).replace("ARCH", pkg_arch).replace("PV", PV).replace("MAJOR", MAJOR_VERSION)
         txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") / 1024))
         txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") / 1024))
         oscmd("mkdir --mode=0755 -p targetroot/DEBIAN")
         oscmd("mkdir --mode=0755 -p targetroot/DEBIAN")
-        oscmd("cd targetroot ; (find usr -type f -exec md5sum {} \;) >  DEBIAN/md5sums")
+        oscmd("cd targetroot && (find usr -type f -exec md5sum {} ;) > DEBIAN/md5sums")
         if (not RUNTIME):
         if (not RUNTIME):
-          oscmd("cd targetroot ; (find etc -type f -exec md5sum {} \;) >> DEBIAN/md5sums")
+          oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums")
           WriteFile("targetroot/DEBIAN/conffiles","/etc/Config.prc\n")
           WriteFile("targetroot/DEBIAN/conffiles","/etc/Config.prc\n")
         WriteFile("targetroot/DEBIAN/postinst","#!/bin/sh\necho running ldconfig\nldconfig\n")
         WriteFile("targetroot/DEBIAN/postinst","#!/bin/sh\necho running ldconfig\nldconfig\n")
         oscmd("cp targetroot/DEBIAN/postinst targetroot/DEBIAN/postrm")
         oscmd("cp targetroot/DEBIAN/postinst targetroot/DEBIAN/postrm")
@@ -6810,7 +6879,7 @@ def MakeInstallerLinux():
 
 
         if RUNTIME:
         if RUNTIME:
             # The runtime doesn't export any useful symbols, so just query the dependencies.
             # The runtime doesn't export any useful symbols, so just query the dependencies.
-            oscmd("cd targetroot; %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals())
+            oscmd("cd targetroot && %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals())
             depends = ReadFile("targetroot/debian/substvars").replace("shlibs:Depends=", "").strip()
             depends = ReadFile("targetroot/debian/substvars").replace("shlibs:Depends=", "").strip()
             recommends = ""
             recommends = ""
         else:
         else:
@@ -6818,12 +6887,12 @@ def MakeInstallerLinux():
             pkg_dir = "debian/panda3d" + MAJOR_VERSION
             pkg_dir = "debian/panda3d" + MAJOR_VERSION
 
 
             # Generate a symbols file so that other packages can know which symbols we export.
             # Generate a symbols file so that other packages can know which symbols we export.
-            oscmd("cd targetroot; dpkg-gensymbols -q -ODEBIAN/symbols -v%(pkg_version)s -p%(pkg_name)s -e%(lib_pattern)s" % locals())
+            oscmd("cd targetroot && dpkg-gensymbols -q -ODEBIAN/symbols -v%(pkg_version)s -p%(pkg_name)s -e%(lib_pattern)s" % locals())
 
 
             # Library dependencies are required, binary dependencies are recommended.
             # Library dependencies are required, binary dependencies are recommended.
             # We explicitly exclude libphysx-extras since we don't want to depend on PhysX.
             # We explicitly exclude libphysx-extras since we don't want to depend on PhysX.
-            oscmd("cd targetroot; LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_dep --ignore-missing-info -x%(pkg_name)s -xlibphysx-extras %(lib_pattern)s" % locals())
-            oscmd("cd targetroot; LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_rec --ignore-missing-info -x%(pkg_name)s %(bin_pattern)s" % locals())
+            oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_dep --ignore-missing-info -x%(pkg_name)s -xlibphysx-extras %(lib_pattern)s" % locals())
+            oscmd("cd targetroot && LD_LIBRARY_PATH=usr/%(lib_dir)s/panda3d %(dpkg_shlibdeps)s -Tdebian/substvars_rec --ignore-missing-info -x%(pkg_name)s %(bin_pattern)s" % locals())
 
 
             # Parse the substvars files generated by dpkg-shlibdeps.
             # Parse the substvars files generated by dpkg-shlibdeps.
             depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip()
             depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip()
@@ -7185,6 +7254,11 @@ try:
             MakeInstallerFreeBSD()
             MakeInstallerFreeBSD()
         else:
         else:
             exit("Do not know how to make an installer for this platform")
             exit("Do not know how to make an installer for this platform")
+
+    if WHEEL:
+        ProgressOutput(100.0, "Building wheel")
+        from makewheel import makewheel
+        makewheel(VERSION, GetOutputDir())
 finally:
 finally:
     SaveDependencyCache()
     SaveDependencyCache()
 
 

+ 51 - 6
makepanda/makepandacore.py

@@ -396,10 +396,16 @@ def CrossCompiling():
     return GetTarget() != GetHost()
     return GetTarget() != GetHost()
 
 
 def GetCC():
 def GetCC():
-    return os.environ.get('CC', TOOLCHAIN_PREFIX + 'gcc')
+    if TARGET == 'darwin':
+        return os.environ.get('CC', TOOLCHAIN_PREFIX + 'clang')
+    else:
+        return os.environ.get('CC', TOOLCHAIN_PREFIX + 'gcc')
 
 
 def GetCXX():
 def GetCXX():
-    return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'g++')
+    if TARGET == 'darwin':
+        return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'clang++')
+    else:
+        return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'g++')
 
 
 def GetStrip():
 def GetStrip():
     # Hack
     # Hack
@@ -512,6 +518,7 @@ def oscmd(cmd, ignoreError = False):
             exit("Cannot find "+exe+" on search path")
             exit("Cannot find "+exe+" on search path")
         res = os.spawnl(os.P_WAIT, exe_path, cmd)
         res = os.spawnl(os.P_WAIT, exe_path, cmd)
     else:
     else:
+        cmd = cmd.replace(';', '\\;')
         res = subprocess.call(cmd, shell=True)
         res = subprocess.call(cmd, shell=True)
         sig = res & 0x7F
         sig = res & 0x7F
         if (GetVerbose() and res != 0):
         if (GetVerbose() and res != 0):
@@ -966,7 +973,12 @@ def ReadBinaryFile(wfile):
         ex = sys.exc_info()[1]
         ex = sys.exc_info()[1]
         exit("Cannot read %s: %s" % (wfile, ex))
         exit("Cannot read %s: %s" % (wfile, ex))
 
 
-def WriteFile(wfile, data):
+def WriteFile(wfile, data, newline=None):
+    if newline is not None:
+        data = data.replace('\r\n', '\n')
+        data = data.replace('\r', '\n')
+        data = data.replace('\n', newline)
+
     try:
     try:
         dsthandle = open(wfile, "w")
         dsthandle = open(wfile, "w")
         dsthandle.write(data)
         dsthandle.write(data)
@@ -984,18 +996,24 @@ def WriteBinaryFile(wfile, data):
         ex = sys.exc_info()[1]
         ex = sys.exc_info()[1]
         exit("Cannot write to %s: %s" % (wfile, ex))
         exit("Cannot write to %s: %s" % (wfile, ex))
 
 
-def ConditionalWriteFile(dest, desiredcontents):
+def ConditionalWriteFile(dest, data, newline=None):
+    if newline is not None:
+        data = data.replace('\r\n', '\n')
+        data = data.replace('\r', '\n')
+        data = data.replace('\n', newline)
+
     try:
     try:
         rfile = open(dest, 'r')
         rfile = open(dest, 'r')
         contents = rfile.read(-1)
         contents = rfile.read(-1)
         rfile.close()
         rfile.close()
     except:
     except:
         contents = 0
         contents = 0
-    if contents != desiredcontents:
+
+    if contents != data:
         if VERBOSE:
         if VERBOSE:
             print("Writing %s" % (dest))
             print("Writing %s" % (dest))
         sys.stdout.flush()
         sys.stdout.flush()
-        WriteFile(dest, desiredcontents)
+        WriteFile(dest, data)
 
 
 def DeleteVCS(dir):
 def DeleteVCS(dir):
     if dir == "": dir = "."
     if dir == "": dir = "."
@@ -1984,6 +2002,11 @@ def SdkLocatePython(prefer_thirdparty_python=False):
          SDK["PYTHONVERSION"] = "python" + ver
          SDK["PYTHONVERSION"] = "python" + ver
          SDK["PYTHONEXEC"] = "/System/Library/Frameworks/Python.framework/Versions/" + ver + "/bin/python" + ver
          SDK["PYTHONEXEC"] = "/System/Library/Frameworks/Python.framework/Versions/" + ver + "/bin/python" + ver
 
 
+         # Avoid choosing the one in the thirdparty package dir.
+         PkgSetCustomLocation("PYTHON")
+         IncDirectory("PYTHON", py_fwx + "/include")
+         LibDirectory("PYTHON", "%s/usr/lib" % (SDK.get("MACOSX", "")))
+
          if sys.version[:3] != ver:
          if sys.version[:3] != ver:
              print("Warning: building with Python %s instead of %s since you targeted a specific Mac OS X version." % (ver, sys.version[:3]))
              print("Warning: building with Python %s instead of %s since you targeted a specific Mac OS X version." % (ver, sys.version[:3]))
 
 
@@ -2057,6 +2080,10 @@ def SdkLocateWindows(version = '7.1'):
         # Choose the latest version of the Windows 10 SDK.
         # Choose the latest version of the Windows 10 SDK.
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
 
 
+        # Fallback in case we can't read the registry.
+        if not platsdk or not os.path.isdir(platsdk):
+            platsdk = "C:\\Program Files (x86)\\Windows Kits\\10\\"
+
         if platsdk and os.path.isdir(platsdk):
         if platsdk and os.path.isdir(platsdk):
             incdirs = glob.glob(os.path.join(platsdk, 'Include', version + '.*.*'))
             incdirs = glob.glob(os.path.join(platsdk, 'Include', version + '.*.*'))
             max_version = ()
             max_version = ()
@@ -2089,6 +2116,10 @@ def SdkLocateWindows(version = '7.1'):
         # We chose a specific version of the Windows 10 SDK.  Verify it exists.
         # We chose a specific version of the Windows 10 SDK.  Verify it exists.
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
 
 
+        # Fallback in case we can't read the registry.
+        if not platsdk or not os.path.isdir(platsdk):
+            platsdk = "C:\\Program Files (x86)\\Windows Kits\\10\\"
+
         if version.count('.') == 2:
         if version.count('.') == 2:
             version += '.0'
             version += '.0'
 
 
@@ -2098,6 +2129,10 @@ def SdkLocateWindows(version = '7.1'):
     elif version == '8.1':
     elif version == '8.1':
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot81")
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot81")
 
 
+        # Fallback in case we can't read the registry.
+        if not platsdk or not os.path.isdir(platsdk):
+            platsdk = "C:\\Program Files (x86)\\Windows Kits\\8.1\\"
+
     elif version == '8.0':
     elif version == '8.0':
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot")
         platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot")
 
 
@@ -2403,9 +2438,19 @@ def SetupVisualStudioEnviron():
     # with Visual Studio 2015 requires use of the Universal CRT.
     # with Visual Studio 2015 requires use of the Universal CRT.
     if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] == '14.0':
     if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] == '14.0':
         win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
         win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
+
+        # Fallback in case we can't read the registry.
+        if not win_kit or not os.path.isdir(win_kit):
+            win_kit = "C:\\Program Files (x86)\\Windows Kits\\10\\"
+        elif not win_kit.endswith('\\'):
+            win_kit += '\\'
+
         AddToPathEnv("LIB", win_kit + "Lib\\10.0.10150.0\\ucrt\\" + arch)
         AddToPathEnv("LIB", win_kit + "Lib\\10.0.10150.0\\ucrt\\" + arch)
         AddToPathEnv("INCLUDE", win_kit + "Include\\10.0.10150.0\\ucrt")
         AddToPathEnv("INCLUDE", win_kit + "Include\\10.0.10150.0\\ucrt")
 
 
+        # Copy the DLLs to the bin directory.
+        CopyAllFiles(GetOutputDir() + "/bin/", win_kit + "Redist\\ucrt\\DLLs\\" + arch + "\\")
+
 ########################################################################
 ########################################################################
 #
 #
 # Include and Lib directories.
 # Include and Lib directories.

+ 598 - 0
makepanda/makewheel.py

@@ -0,0 +1,598 @@
+"""
+Generates a wheel (.whl) file from the output of makepanda.
+
+Since the wheel requires special linking, this will only work if compiled with
+the `--wheel` parameter.
+
+Please keep this file work with Panda3D 1.9 until that reaches EOL.
+"""
+from __future__ import print_function, unicode_literals
+from distutils.util import get_platform
+import json
+
+import sys
+import os
+from os.path import join
+import shutil
+import zipfile
+import hashlib
+import tempfile
+import subprocess
+from distutils.sysconfig import get_config_var
+from optparse import OptionParser
+from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose
+from base64 import urlsafe_b64encode
+
+
+default_platform = get_platform()
+
+if default_platform.startswith("linux-"):
+    # Is this manylinux1?
+    if os.path.isfile("/lib/libc-2.5.so") and os.path.isdir("/opt/python"):
+        default_platform = default_platform.replace("linux", "manylinux1")
+
+
+def get_abi_tag():
+    if sys.version_info >= (3, 0):
+        soabi = get_config_var('SOABI')
+        if soabi and soabi.startswith('cpython-'):
+            return 'cp' + soabi.split('-')[1]
+        elif soabi:
+            return soabi.replace('.', '_').replace('-', '_')
+
+    soabi = 'cp%d%d' % (sys.version_info[:2])
+
+    debug_flag = get_config_var('Py_DEBUG')
+    if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag:
+        soabi += 'd'
+
+    malloc_flag = get_config_var('WITH_PYMALLOC')
+    if malloc_flag is None or malloc_flag:
+        soabi += 'm'
+
+    if sys.version_info < (3, 3):
+        usize = get_config_var('Py_UNICODE_SIZE')
+        if (usize is None and sys.maxunicode == 0x10ffff) or usize == 4:
+            soabi += 'u'
+
+    return soabi
+
+
+def is_exe_file(path):
+    return os.path.isfile(path) and path.lower().endswith('.exe')
+
+
+def is_elf_file(path):
+    base = os.path.basename(path)
+    return os.path.isfile(path) and '.' not in base and \
+           open(path, 'rb').read(4) == b'\x7FELF'
+
+
+def is_mach_o_file(path):
+    base = os.path.basename(path)
+    return os.path.isfile(path) and '.' not in base and \
+           open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA',
+                                        b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE',
+                                        b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE')
+
+def is_fat_file(path):
+    return os.path.isfile(path) and \
+           open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA')
+
+
+if sys.platform in ('win32', 'cygwin'):
+    is_executable = is_exe_file
+elif sys.platform == 'darwin':
+    is_executable = is_mach_o_file
+else:
+    is_executable = is_elf_file
+
+
+# Other global parameters
+PY_VERSION = "cp{0}{1}".format(*sys.version_info)
+ABI_TAG = get_abi_tag()
+EXCLUDE_EXT = [".pyc", ".pyo", ".N", ".prebuilt", ".xcf", ".plist", ".vcproj", ".sln"]
+
+# Plug-ins to install.
+PLUGIN_LIBS = ["pandagl", "pandagles", "pandagles2", "pandadx9", "p3tinydisplay", "p3ptloader", "p3assimp", "p3ffmpeg", "p3openal_audio", "p3fmod_audio"]
+
+WHEEL_DATA = """Wheel-Version: 1.0
+Generator: makepanda
+Root-Is-Purelib: false
+Tag: {0}-{1}-{2}
+"""
+
+METADATA = {
+    "license": "BSD",
+    "name": "Panda3D",
+    "metadata_version": "2.0",
+    "generator": "makepanda",
+    "summary": "Panda3D is a game engine, a framework for 3D rendering and "
+               "game development for Python and C++ programs.",
+    "extensions": {
+        "python.details": {
+            "project_urls": {
+                "Home": "https://www.panda3d.org/"
+            },
+            "document_names": {
+                "license": "LICENSE.txt"
+            },
+            "contacts": [
+                {
+                    "role": "author",
+                    "email": "[email protected]",
+                    "name": "Panda3D Team"
+                }
+            ]
+        }
+    },
+    "classifiers": [
+        "Development Status :: 5 - Production/Stable",
+        "Intended Audience :: Developers",
+        "Intended Audience :: End Users/Desktop",
+        "License :: OSI Approved :: BSD License",
+        "Operating System :: OS Independent",
+        "Programming Language :: C++",
+        "Programming Language :: Python",
+        "Topic :: Games/Entertainment",
+        "Topic :: Multimedia",
+        "Topic :: Multimedia :: Graphics",
+        "Topic :: Multimedia :: Graphics :: 3D Rendering"
+    ]
+}
+
+PANDA3D_TOOLS_INIT = """import os, sys
+import panda3d
+
+if sys.platform in ('win32', 'cygwin'):
+    path_var = 'PATH'
+elif sys.platform == 'darwin':
+    path_var = 'DYLD_LIBRARY_PATH'
+else:
+    path_var = 'LD_LIBRARY_PATH'
+
+dir = os.path.dirname(panda3d.__file__)
+del panda3d
+if not os.environ.get(path_var):
+    os.environ[path_var] = dir
+else:
+    os.environ[path_var] = dir + os.pathsep + os.environ[path_var]
+
+del os, sys, path_var, dir
+
+
+def _exec_tool(tool):
+    import os, sys
+    from subprocess import Popen
+    tools_dir = os.path.dirname(__file__)
+    handle = Popen(sys.argv, executable=os.path.join(tools_dir, tool))
+    try:
+        try:
+            return handle.wait()
+        except KeyboardInterrupt:
+            # Give the program a chance to handle the signal gracefully.
+            return handle.wait()
+    except:
+        handle.kill()
+        handle.wait()
+        raise
+
+# Register all the executables in this directory as global functions.
+{0}
+"""
+
+
+def parse_dependencies_windows(data):
+    """ Parses the given output from dumpbin /dependents to determine the list
+    of dll's this executable file depends on. """
+
+    lines = data.splitlines()
+    li = 0
+    while li < len(lines):
+        line = lines[li]
+        li += 1
+        if line.find(' has the following dependencies') != -1:
+            break
+
+    if li < len(lines):
+        line = lines[li]
+        if line.strip() == '':
+            # Skip a blank line.
+            li += 1
+
+    # Now we're finding filenames, until the next blank line.
+    filenames = []
+    while li < len(lines):
+        line = lines[li]
+        li += 1
+        line = line.strip()
+        if line == '':
+            # We're done.
+            return filenames
+        filenames.append(line)
+
+    # At least we got some data.
+    return filenames
+
+
+def parse_dependencies_unix(data):
+    """ Parses the given output from otool -XL or ldd to determine the list of
+    libraries this executable file depends on. """
+
+    lines = data.splitlines()
+    filenames = []
+    for l in lines:
+        l = l.strip()
+        if l != "statically linked":
+            filenames.append(l.split(' ', 1)[0])
+    return filenames
+
+
+def scan_dependencies(pathname):
+    """ Checks the named file for DLL dependencies, and adds any appropriate
+    dependencies found into pluginDependencies and dependentFiles. """
+
+    if sys.platform == "darwin":
+        command = ['otool', '-XL', pathname]
+    elif sys.platform in ("win32", "cygwin"):
+        command = ['dumpbin', '/dependents', pathname]
+    else:
+        command = ['ldd', pathname]
+
+    process = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True)
+    output, unused_err = process.communicate()
+    retcode = process.poll()
+    if retcode:
+        raise subprocess.CalledProcessError(retcode, command[0], output=output)
+    filenames = None
+
+    if sys.platform in ("win32", "cygwin"):
+        filenames = parse_dependencies_windows(output)
+    else:
+        filenames = parse_dependencies_unix(output)
+
+    if filenames is None:
+        sys.exit("Unable to determine dependencies from %s" % (pathname))
+
+    if sys.platform == "darwin" and len(filenames) > 0:
+        # Filter out the library ID.
+        if os.path.basename(filenames[0]).split('.', 1)[0] == os.path.basename(pathname).split('.', 1)[0]:
+            del filenames[0]
+
+    return filenames
+
+
+class WheelFile(object):
+    def __init__(self, name, version, platform):
+        self.name = name
+        self.version = version
+        self.platform = platform
+
+        wheel_name = "{0}-{1}-{2}-{3}-{4}.whl".format(
+            name, version, PY_VERSION, ABI_TAG, platform)
+
+        print("Writing %s" % (wheel_name))
+        self.zip_file = zipfile.ZipFile(wheel_name, 'w', zipfile.ZIP_DEFLATED)
+        self.records = []
+
+        # Used to locate dependency libraries.
+        self.lib_path = []
+        self.dep_paths = {}
+
+    def consider_add_dependency(self, target_path, dep, search_path=None):
+        """Considers adding a dependency library.
+        Returns the target_path if it was added, which may be different from
+        target_path if it was already added earlier, or None if it wasn't."""
+
+        if dep in self.dep_paths:
+            # Already considered this.
+            return self.dep_paths[dep]
+
+        self.dep_paths[dep] = None
+
+        if dep.lower().startswith("python") or os.path.basename(dep).startswith("libpython"):
+            # Don't include the Python library.
+            return
+
+        if sys.platform == "darwin" and dep.endswith(".so"):
+            # Temporary hack for 1.9, which had link deps on modules.
+            return
+
+        source_path = None
+
+        if search_path is None:
+            search_path = self.lib_path
+
+        for lib_dir in search_path:
+            # Ignore static stuff.
+            path = os.path.join(lib_dir, dep)
+            if os.path.isfile(path):
+                source_path = os.path.normpath(path)
+                break
+
+        if not source_path:
+            # Couldn't find library in the panda3d lib dir.
+            #print("Ignoring %s" % (dep))
+            return
+
+        self.dep_paths[dep] = target_path
+        self.write_file(target_path, source_path)
+        return target_path
+
+    def write_file(self, target_path, source_path):
+        """Adds the given file to the .whl file."""
+
+        # If this is a .so file, we should set the rpath appropriately.
+        temp = None
+        ext = os.path.splitext(source_path)[1]
+        if ext in ('.so', '.dylib') or '.so.' in os.path.basename(source_path) or \
+            (not ext and is_executable(source_path)):
+            # Scan and add Unix dependencies.
+            deps = scan_dependencies(source_path)
+            for dep in deps:
+                # Only include dependencies with relative path.  Otherwise we
+                # end up overwriting system files like /lib/ld-linux.so.2!
+                # Yes, it happened to me.
+                if '/' not in dep:
+                    target_dep = os.path.dirname(target_path) + '/' + dep
+                    self.consider_add_dependency(target_dep, dep)
+
+            suffix = ''
+            if '.so' in os.path.basename(source_path):
+                suffix = '.so'
+            elif ext == '.dylib':
+                suffix = '.dylib'
+
+            temp = tempfile.NamedTemporaryFile(suffix=suffix, prefix='whl', delete=False)
+
+            # On macOS, if no fat wheel was requested, extract the right architecture.
+            if sys.platform == "darwin" and is_fat_file(source_path) and not self.platform.endswith("_intel"):
+                if self.platform.endswith("_x86_64"):
+                    arch = 'x86_64'
+                else:
+                    arch = self.platform.split('_')[-1]
+                subprocess.call(['lipo', source_path, '-extract', arch, '-output', temp.name])
+            else:
+                # Otherwise, just copy it over.
+                temp.write(open(source_path, 'rb').read())
+
+            temp.write(open(source_path, 'rb').read())
+            os.fchmod(temp.fileno(), os.fstat(temp.fileno()).st_mode | 0o111)
+            temp.close()
+
+            # Fix things like @loader_path/../lib references
+            if sys.platform == "darwin":
+                loader_path = [os.path.dirname(source_path)]
+                for dep in deps:
+                    if '@loader_path' not in dep:
+                        continue
+
+                    dep_path = dep.replace('@loader_path', '.')
+                    target_dep = os.path.dirname(target_path) + '/' + os.path.basename(dep)
+                    target_dep = self.consider_add_dependency(target_dep, dep_path, loader_path)
+                    if not target_dep:
+                        # It won't be included, so no use adjusting the path.
+                        continue
+
+                    new_dep = os.path.join('@loader_path', os.path.relpath(target_dep, os.path.dirname(target_path)))
+                    subprocess.call(["install_name_tool", "-change", dep, new_dep, temp.name])
+            else:
+                subprocess.call(["strip", "-s", temp.name])
+                subprocess.call(["patchelf", "--set-rpath", "$ORIGIN", temp.name])
+
+            source_path = temp.name
+
+        ext = ext.lower()
+        if ext in ('.dll', '.pyd', '.exe'):
+            # Scan and add Win32 dependencies.
+            for dep in scan_dependencies(source_path):
+                target_dep = os.path.dirname(target_path) + '/' + dep
+                self.consider_add_dependency(target_dep, dep)
+
+        # Calculate the SHA-256 hash and size.
+        sha = hashlib.sha256()
+        fp = open(source_path, 'rb')
+        size = 0
+        data = fp.read(1024 * 1024)
+        while data:
+            size += len(data)
+            sha.update(data)
+            data = fp.read(1024 * 1024)
+        fp.close()
+
+        # Save it in PEP-0376 format for writing out later.
+        digest = str(urlsafe_b64encode(sha.digest()))
+        digest = digest.rstrip('=')
+        self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, size))
+
+        if GetVerbose():
+            print("Adding %s from %s" % (target_path, source_path))
+        self.zip_file.write(source_path, target_path)
+
+        #if temp:
+        #    os.unlink(temp.name)
+
+    def write_file_data(self, target_path, source_data):
+        """Adds the given file from a string."""
+
+        sha = hashlib.sha256()
+        sha.update(source_data.encode())
+        digest = str(urlsafe_b64encode(sha.digest()))
+        digest = digest.rstrip('=')
+        self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, len(source_data)))
+
+        if GetVerbose():
+            print("Adding %s from data" % target_path)
+        self.zip_file.writestr(target_path, source_data)
+
+    def write_directory(self, target_dir, source_dir):
+        """Adds the given directory recursively to the .whl file."""
+
+        for root, dirs, files in os.walk(source_dir):
+            for file in files:
+                if os.path.splitext(file)[1] in EXCLUDE_EXT:
+                    continue
+
+                source_path = os.path.join(root, file)
+                target_path = os.path.join(target_dir, os.path.relpath(source_path, source_dir))
+                target_path = target_path.replace('\\', '/')
+                self.write_file(target_path, source_path)
+
+    def close(self):
+        # Write the RECORD file.
+        record_file = "{0}-{1}.dist-info/RECORD".format(self.name, self.version)
+        self.records.append(record_file + ",,\n")
+
+        self.zip_file.writestr(record_file, "".join(self.records))
+        self.zip_file.close()
+
+
+def makewheel(version, output_dir, platform=default_platform):
+    if sys.platform not in ("win32", "darwin") and not sys.platform.startswith("cygwin"):
+        if not LocateBinary("patchelf"):
+            raise Exception("patchelf is required when building a Linux wheel.")
+
+    platform = platform.replace('-', '_').replace('.', '_')
+
+    # Global filepaths
+    panda3d_dir = join(output_dir, "panda3d")
+    pandac_dir = join(output_dir, "pandac")
+    direct_dir = join(output_dir, "direct")
+    models_dir = join(output_dir, "models")
+    etc_dir = join(output_dir, "etc")
+    bin_dir = join(output_dir, "bin")
+    if sys.platform == "win32":
+        libs_dir = join(output_dir, "bin")
+    else:
+        libs_dir = join(output_dir, "lib")
+    license_src = "LICENSE"
+    readme_src = "README.md"
+
+    # Update relevant METADATA entries
+    METADATA['version'] = version
+    version_classifiers = [
+        "Programming Language :: Python :: {0}".format(*sys.version_info),
+        "Programming Language :: Python :: {0}.{1}".format(*sys.version_info),
+    ]
+    METADATA['classifiers'].extend(version_classifiers)
+
+    # Build out the metadata
+    details = METADATA["extensions"]["python.details"]
+    homepage = details["project_urls"]["Home"]
+    author = details["contacts"][0]["name"]
+    email = details["contacts"][0]["email"]
+    metadata = ''.join([
+        "Metadata-Version: {metadata_version}\n" \
+        "Name: {name}\n" \
+        "Version: {version}\n" \
+        "Summary: {summary}\n" \
+        "License: {license}\n".format(**METADATA),
+        "Home-page: {0}\n".format(homepage),
+        "Author: {0}\n".format(author),
+        "Author-email: {0}\n".format(email),
+        "Platform: {0}\n".format(platform),
+    ] + ["Classifier: {0}\n".format(c) for c in METADATA['classifiers']])
+
+    # Zip it up and name it the right thing
+    whl = WheelFile('panda3d', version, platform)
+    whl.lib_path = [libs_dir]
+
+    # Add the trees with Python modules.
+    whl.write_directory('direct', direct_dir)
+
+    # Write the panda3d tree.  We use a custom empty __init__ since the
+    # default one adds the bin directory to the PATH, which we don't have.
+    whl.write_file_data('panda3d/__init__.py', '')
+
+    ext_suffix = GetExtensionSuffix()
+
+    for file in os.listdir(panda3d_dir):
+        if file == '__init__.py':
+            pass
+        elif file.endswith(ext_suffix) or file.endswith('.py'):
+            source_path = os.path.join(panda3d_dir, file)
+
+            if file.endswith('.pyd') and platform.startswith('cygwin'):
+                # Rename it to .dll for cygwin Python to be able to load it.
+                target_path = 'panda3d/' + os.path.splitext(file)[0] + '.dll'
+            else:
+                target_path = 'panda3d/' + file
+
+            whl.write_file(target_path, source_path)
+
+    # Add plug-ins.
+    for lib in PLUGIN_LIBS:
+        plugin_name = 'lib' + lib
+        if sys.platform in ('win32', 'cygwin'):
+            plugin_name += '.dll'
+        elif sys.platform == 'darwin':
+            plugin_name += '.dylib'
+        else:
+            plugin_name += '.so'
+        plugin_path = os.path.join(libs_dir, plugin_name)
+        if os.path.isfile(plugin_path):
+            whl.write_file('panda3d/' + plugin_name, plugin_path)
+
+    # Add the .data directory, containing additional files.
+    data_dir = 'panda3d-{0}.data'.format(version)
+    #whl.write_directory(data_dir + '/data/etc', etc_dir)
+    #whl.write_directory(data_dir + '/data/models', models_dir)
+
+    # Actually, let's not.  That seems to install the files to the strangest
+    # places in the user's filesystem.  Let's instead put them in panda3d.
+    whl.write_directory('panda3d/etc', etc_dir)
+    whl.write_directory('panda3d/models', models_dir)
+
+    # Add the pandac tree for backward compatibility.
+    for file in os.listdir(pandac_dir):
+        if file.endswith('.py'):
+            whl.write_file('pandac/' + file, os.path.join(pandac_dir, file))
+
+    # Add a panda3d-tools directory containing the executables.
+    entry_points = '[console_scripts]\n'
+    entry_points += 'eggcacher = direct.directscripts.eggcacher:main\n'
+    entry_points += 'packpanda = direct.directscripts.packpanda:main\n'
+    tools_init = ''
+    for file in os.listdir(bin_dir):
+        basename = os.path.splitext(file)[0]
+        if basename in ('eggcacher', 'packpanda'):
+            continue
+
+        source_path = os.path.join(bin_dir, file)
+
+        if is_executable(source_path):
+            # Put the .exe files inside the panda3d-tools directory.
+            whl.write_file('panda3d_tools/' + file, source_path)
+
+            # Tell pip to create a wrapper script.
+            funcname = basename.replace('-', '_')
+            entry_points += '{0} = panda3d_tools:{1}\n'.format(basename, funcname)
+            tools_init += '{0} = lambda: _exec_tool({1!r})\n'.format(funcname, file)
+
+    whl.write_file_data('panda3d_tools/__init__.py', PANDA3D_TOOLS_INIT.format(tools_init))
+
+    # Add the dist-info directory last.
+    info_dir = 'panda3d-{0}.dist-info'.format(version)
+    whl.write_file_data(info_dir + '/entry_points.txt', entry_points)
+    whl.write_file_data(info_dir + '/metadata.json', json.dumps(METADATA, indent=4, separators=(',', ': ')))
+    whl.write_file_data(info_dir + '/METADATA', metadata)
+    whl.write_file_data(info_dir + '/WHEEL', WHEEL_DATA.format(PY_VERSION, ABI_TAG, platform))
+    whl.write_file(info_dir + '/LICENSE.txt', license_src)
+    whl.write_file(info_dir + '/README.md', readme_src)
+    whl.write_file_data(info_dir + '/top_level.txt', 'direct\npanda3d\npandac\npanda3d_tools\n')
+
+    whl.close()
+
+
+if __name__ == "__main__":
+    version = ParsePandaVersion("dtool/PandaVersion.pp")
+
+    parser = OptionParser()
+    parser.add_option('', '--version', dest = 'version', help = 'Panda3D version number (default: %s)' % (version), default = version)
+    parser.add_option('', '--outputdir', dest = 'outputdir', help = 'Makepanda\'s output directory (default: built)', default = 'built')
+    parser.add_option('', '--verbose', dest = 'verbose', help = 'Enable verbose output', action = 'store_true', default = False)
+    parser.add_option('', '--platform', dest = 'platform', help = 'Override platform tag (default: %s)' % (default_platform), default = get_platform())
+    (options, args) = parser.parse_args()
+
+    SetVerbose(options.verbose)
+    makewheel(options.version, options.outputdir, options.platform)

+ 10 - 1
panda/src/bullet/bulletBodyNode.cxx

@@ -45,7 +45,16 @@ BulletBodyNode(const BulletBodyNode &copy) :
   _shapes(copy._shapes)
   _shapes(copy._shapes)
 {
 {
   if (copy._shape && copy._shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE) {
   if (copy._shape && copy._shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE) {
-    _shape = new btCompoundShape(copy._shape);
+    // btCompoundShape does not define a copy constructor.  Manually copy.
+    btCompoundShape *shape = new btCompoundShape;
+    _shape = shape;
+
+    btCompoundShape *copy_shape = (btCompoundShape *)copy._shape;
+    int num_children = copy_shape->getNumChildShapes();
+    for (int i = 0; i < num_children; ++i) {
+      shape->addChildShape(copy_shape->getChildTransform(i),
+                           copy_shape->getChildShape(i));
+    }
   }
   }
   else if (copy._shape && copy._shape->getShapeType() == EMPTY_SHAPE_PROXYTYPE) {
   else if (copy._shape && copy._shape->getShapeType() == EMPTY_SHAPE_PROXYTYPE) {
     _shape = new btEmptyShape();
     _shape = new btEmptyShape();

+ 1 - 1
panda/src/bullet/bulletGhostNode.cxx

@@ -55,7 +55,7 @@ void BulletGhostNode::
 parents_changed() {
 parents_changed() {
 
 
   Parents parents = get_parents();
   Parents parents = get_parents();
-  for (int i=0; i < parents.get_num_parents(); ++i) {
+  for (size_t i = 0; i < parents.get_num_parents(); ++i) {
     PandaNode *parent = parents.get_parent(i);
     PandaNode *parent = parents.get_parent(i);
     TypeHandle type = parent->get_type();
     TypeHandle type = parent->get_type();
 
 

+ 0 - 1
panda/src/bullet/bulletRigidBodyNode.I

@@ -18,7 +18,6 @@ INLINE BulletRigidBodyNode::
 ~BulletRigidBodyNode() {
 ~BulletRigidBodyNode() {
 
 
   delete _rigid;
   delete _rigid;
-  delete _motion;
 }
 }
 
 
 /**
 /**

+ 8 - 12
panda/src/bullet/bulletRigidBodyNode.cxx

@@ -21,16 +21,12 @@ TypeHandle BulletRigidBodyNode::_type_handle;
  */
  */
 BulletRigidBodyNode::
 BulletRigidBodyNode::
 BulletRigidBodyNode(const char *name) : BulletBodyNode(name) {
 BulletRigidBodyNode(const char *name) : BulletBodyNode(name) {
-
-  // Motion state
-  _motion = new MotionState();
-
   // Mass properties
   // Mass properties
   btScalar mass(0.0);
   btScalar mass(0.0);
   btVector3 inertia(0, 0, 0);
   btVector3 inertia(0, 0, 0);
 
 
   // construction info
   // construction info
-  btRigidBody::btRigidBodyConstructionInfo ci(mass, _motion, _shape, inertia);
+  btRigidBody::btRigidBodyConstructionInfo ci(mass, &_motion, _shape, inertia);
 
 
   // Additional damping
   // Additional damping
   if (bullet_additional_damping) {
   if (bullet_additional_damping) {
@@ -52,13 +48,13 @@ BulletRigidBodyNode(const char *name) : BulletBodyNode(name) {
  */
  */
 BulletRigidBodyNode::
 BulletRigidBodyNode::
 BulletRigidBodyNode(const BulletRigidBodyNode &copy) :
 BulletRigidBodyNode(const BulletRigidBodyNode &copy) :
-  BulletBodyNode(copy)
+  BulletBodyNode(copy),
+  _motion(copy._motion)
 {
 {
-  _motion = new MotionState(*copy._motion);
   _rigid = new btRigidBody(*copy._rigid);
   _rigid = new btRigidBody(*copy._rigid);
   _rigid->setUserPointer(this);
   _rigid->setUserPointer(this);
   _rigid->setCollisionShape(_shape);
   _rigid->setCollisionShape(_shape);
-  _rigid->setMotionState(_motion);
+  _rigid->setMotionState(&_motion);
 }
 }
 
 
 /**
 /**
@@ -280,7 +276,7 @@ apply_central_impulse(const LVector3 &impulse) {
 void BulletRigidBodyNode::
 void BulletRigidBodyNode::
 transform_changed() {
 transform_changed() {
 
 
-  if (_motion->sync_disabled()) return;
+  if (_motion.sync_disabled()) return;
 
 
   NodePath np = NodePath::any_path((PandaNode *)this);
   NodePath np = NodePath::any_path((PandaNode *)this);
   CPT(TransformState) ts = np.get_net_transform();
   CPT(TransformState) ts = np.get_net_transform();
@@ -290,7 +286,7 @@ transform_changed() {
   // transform within the motion state.  For dynamic bodies we need to store
   // transform within the motion state.  For dynamic bodies we need to store
   // the net scale within the motion state, since Bullet might update the
   // the net scale within the motion state, since Bullet might update the
   // transform via MotionState::setWorldTransform.
   // transform via MotionState::setWorldTransform.
-  _motion->set_net_transform(ts);
+  _motion.set_net_transform(ts);
 
 
   // For dynamic or static bodies we directly apply the new transform.
   // For dynamic or static bodies we directly apply the new transform.
   if (!is_kinematic()) {
   if (!is_kinematic()) {
@@ -334,7 +330,7 @@ sync_p2b() {
 void BulletRigidBodyNode::
 void BulletRigidBodyNode::
 sync_b2p() {
 sync_b2p() {
 
 
-  _motion->sync_b2p((PandaNode *)this);
+  _motion.sync_b2p((PandaNode *)this);
 }
 }
 
 
 /**
 /**
@@ -589,7 +585,7 @@ pick_dirty_flag() {
 bool BulletRigidBodyNode::
 bool BulletRigidBodyNode::
 pick_dirty_flag() {
 pick_dirty_flag() {
 
 
-  return _motion->pick_dirty_flag();
+  return _motion.pick_dirty_flag();
 }
 }
 
 
 /**
 /**

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

@@ -124,7 +124,7 @@ private:
     bool _was_dirty;
     bool _was_dirty;
   };
   };
 
 
-  MotionState *_motion;
+  MotionState _motion;
   btRigidBody *_rigid;
   btRigidBody *_rigid;
 
 
 public:
 public:

+ 14 - 7
panda/src/cocoadisplay/cocoaGraphicsPipe.mm

@@ -140,11 +140,14 @@ load_display_information() {
   // _display_information->_device_id = CGDisplaySerialNumber(_display);
   // _display_information->_device_id = CGDisplaySerialNumber(_display);
 
 
   // Display modes
   // Display modes
+  size_t num_modes = 0;
 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   CFArrayRef modes = CGDisplayCopyAllDisplayModes(_display, NULL);
   CFArrayRef modes = CGDisplayCopyAllDisplayModes(_display, NULL);
-  size_t num_modes = CFArrayGetCount(modes);
-  _display_information->_total_display_modes = num_modes;
-  _display_information->_display_mode_array = new DisplayMode[num_modes];
+  if (modes != NULL) {
+    num_modes = CFArrayGetCount(modes);
+    _display_information->_total_display_modes = num_modes;
+    _display_information->_display_mode_array = new DisplayMode[num_modes];
+  }
 
 
   for (size_t i = 0; i < num_modes; ++i) {
   for (size_t i = 0; i < num_modes; ++i) {
     CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
     CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
@@ -181,13 +184,17 @@ load_display_information() {
     }
     }
     CFRelease(encoding);
     CFRelease(encoding);
   }
   }
-  CFRelease(modes);
+  if (modes != NULL) {
+    CFRelease(modes);
+  }
 
 
 #else
 #else
   CFArrayRef modes = CGDisplayAvailableModes(_display);
   CFArrayRef modes = CGDisplayAvailableModes(_display);
-  size_t num_modes = CFArrayGetCount(modes);
-  _display_information->_total_display_modes = num_modes;
-  _display_information->_display_mode_array = new DisplayMode[num_modes];
+  if (modes != NULL) {
+    num_modes = CFArrayGetCount(modes);
+    _display_information->_total_display_modes = num_modes;
+    _display_information->_display_mode_array = new DisplayMode[num_modes];
+  }
 
 
   for (size_t i = 0; i < num_modes; ++i) {
   for (size_t i = 0; i < num_modes; ++i) {
     CFDictionaryRef mode = (CFDictionaryRef) CFArrayGetValueAtIndex(modes, i);
     CFDictionaryRef mode = (CFDictionaryRef) CFArrayGetValueAtIndex(modes, i);

+ 3 - 1
panda/src/display/frameBufferProperties.cxx

@@ -659,7 +659,7 @@ setup_color_texture(Texture *tex) const {
   // as the below one would be generated dynamically by the GSG to reflect the
   // as the below one would be generated dynamically by the GSG to reflect the
   // formats that are supported for render-to-texture.
   // formats that are supported for render-to-texture.
 
 
-  static const int num_formats = 15;
+  static const int num_formats = 17;
   static const struct {
   static const struct {
     unsigned char color_bits, red_bits, green_bits, blue_bits, alpha_bits;
     unsigned char color_bits, red_bits, green_bits, blue_bits, alpha_bits;
     bool has_float;
     bool has_float;
@@ -669,6 +669,8 @@ setup_color_texture(Texture *tex) const {
     {  1,  1,  1,  0,  0, false, Texture::F_rg },
     {  1,  1,  1,  0,  0, false, Texture::F_rg },
     {  1,  1,  1,  1,  0, false, Texture::F_rgb },
     {  1,  1,  1,  1,  0, false, Texture::F_rgb },
     {  1,  1,  1,  1,  1, false, Texture::F_rgba },
     {  1,  1,  1,  1,  1, false, Texture::F_rgba },
+    {  8,  8,  0,  0,  0, false, Texture::F_red },
+    { 16,  8,  8,  0,  0, false, Texture::F_rg },
     { 24,  8,  8,  8,  0, false, Texture::F_rgb8 },
     { 24,  8,  8,  8,  0, false, Texture::F_rgb8 },
     { 32,  8,  8,  8,  8, false, Texture::F_rgba8 },
     { 32,  8,  8,  8,  8, false, Texture::F_rgba8 },
     { 16, 16,  0,  0,  0,  true, Texture::F_r16 },
     { 16, 16,  0,  0,  0,  true, Texture::F_r16 },

+ 0 - 13
panda/src/display/get_x11.h

@@ -49,19 +49,6 @@ struct XVisualInfo;
 #include <X11/Xutil.h>
 #include <X11/Xutil.h>
 #include <X11/keysym.h>
 #include <X11/keysym.h>
 #include <X11/Xatom.h>
 #include <X11/Xatom.h>
-
-#ifdef HAVE_XRANDR
-#include <X11/extensions/Xrandr.h>
-#endif  // HAVE_XRANDR
-
-#ifdef HAVE_XCURSOR
-#include <X11/Xcursor/Xcursor.h>
-#endif
-
-#ifdef HAVE_XF86DGA
-#include <X11/extensions/Xxf86dga.h>
-#endif
-
 #include "post_x11_include.h"
 #include "post_x11_include.h"
 
 
 #endif  // CPPPARSER
 #endif  // CPPPARSER

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

@@ -62,6 +62,14 @@ release_all_index_buffers() {
   return _prepared_objects->release_all_index_buffers();
   return _prepared_objects->release_all_index_buffers();
 }
 }
 
 
+/**
+ * Frees the resources for all index buffers associated with this GSG.
+ */
+INLINE int GraphicsStateGuardian::
+release_all_shader_buffers() {
+  return _prepared_objects->release_all_shader_buffers();
+}
+
 /**
 /**
  * Sets the active flag associated with the GraphicsStateGuardian.  If the
  * Sets the active flag associated with the GraphicsStateGuardian.  If the
  * GraphicsStateGuardian is marked inactive, nothing is rendered.  This is not
  * GraphicsStateGuardian is marked inactive, nothing is rendered.  This is not

+ 118 - 35
panda/src/display/graphicsStateGuardian.cxx

@@ -62,12 +62,15 @@
 #include <algorithm>
 #include <algorithm>
 #include <limits.h>
 #include <limits.h>
 
 
-PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Vertex buffer switch:Vertex");
-PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Vertex buffer switch:Index");
+PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Buffer switch:Vertex");
+PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Buffer switch:Index");
+PStatCollector GraphicsStateGuardian::_shader_buffer_switch_pcollector("Buffer switch:Shader");
 PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer");
 PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer");
 PStatCollector GraphicsStateGuardian::_load_index_buffer_pcollector("Draw:Transfer data:Index buffer");
 PStatCollector GraphicsStateGuardian::_load_index_buffer_pcollector("Draw:Transfer data:Index buffer");
+PStatCollector GraphicsStateGuardian::_load_shader_buffer_pcollector("Draw:Transfer data:Shader buffer");
 PStatCollector GraphicsStateGuardian::_create_vertex_buffer_pcollector("Draw:Transfer data:Create Vertex buffer");
 PStatCollector GraphicsStateGuardian::_create_vertex_buffer_pcollector("Draw:Transfer data:Create Vertex buffer");
 PStatCollector GraphicsStateGuardian::_create_index_buffer_pcollector("Draw:Transfer data:Create Index buffer");
 PStatCollector GraphicsStateGuardian::_create_index_buffer_pcollector("Draw:Transfer data:Create Index buffer");
+PStatCollector GraphicsStateGuardian::_create_shader_buffer_pcollector("Draw:Transfer data:Create Shader buffer");
 PStatCollector GraphicsStateGuardian::_load_texture_pcollector("Draw:Transfer data:Texture");
 PStatCollector GraphicsStateGuardian::_load_texture_pcollector("Draw:Transfer data:Texture");
 PStatCollector GraphicsStateGuardian::_data_transferred_pcollector("Data transferred");
 PStatCollector GraphicsStateGuardian::_data_transferred_pcollector("Data transferred");
 PStatCollector GraphicsStateGuardian::_texmgrmem_total_pcollector("Texture manager");
 PStatCollector GraphicsStateGuardian::_texmgrmem_total_pcollector("Texture manager");
@@ -104,6 +107,7 @@ PStatCollector GraphicsStateGuardian::_prepare_geom_pcollector("Draw:Prepare:Geo
 PStatCollector GraphicsStateGuardian::_prepare_shader_pcollector("Draw:Prepare:Shader");
 PStatCollector GraphicsStateGuardian::_prepare_shader_pcollector("Draw:Prepare:Shader");
 PStatCollector GraphicsStateGuardian::_prepare_vertex_buffer_pcollector("Draw:Prepare:Vertex buffer");
 PStatCollector GraphicsStateGuardian::_prepare_vertex_buffer_pcollector("Draw:Prepare:Vertex buffer");
 PStatCollector GraphicsStateGuardian::_prepare_index_buffer_pcollector("Draw:Prepare:Index buffer");
 PStatCollector GraphicsStateGuardian::_prepare_index_buffer_pcollector("Draw:Prepare:Index buffer");
+PStatCollector GraphicsStateGuardian::_prepare_shader_buffer_pcollector("Draw:Prepare:Shader buffer");
 
 
 PStatCollector GraphicsStateGuardian::_draw_set_state_transform_pcollector("Draw:Set State:Transform");
 PStatCollector GraphicsStateGuardian::_draw_set_state_transform_pcollector("Draw:Set State:Transform");
 PStatCollector GraphicsStateGuardian::_draw_set_state_alpha_test_pcollector("Draw:Set State:Alpha test");
 PStatCollector GraphicsStateGuardian::_draw_set_state_alpha_test_pcollector("Draw:Set State:Alpha test");
@@ -147,6 +151,10 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _coordinate_system = CS_invalid;
   _coordinate_system = CS_invalid;
   _internal_transform = TransformState::make_identity();
   _internal_transform = TransformState::make_identity();
 
 
+  if (_internal_coordinate_system == CS_default) {
+    _internal_coordinate_system = get_default_coordinate_system();
+  }
+
   set_coordinate_system(get_default_coordinate_system());
   set_coordinate_system(get_default_coordinate_system());
 
 
   _data_reader = (GeomVertexDataPipelineReader *)NULL;
   _data_reader = (GeomVertexDataPipelineReader *)NULL;
@@ -653,6 +661,22 @@ void GraphicsStateGuardian::
 release_index_buffer(IndexBufferContext *) {
 release_index_buffer(IndexBufferContext *) {
 }
 }
 
 
+/**
+ * Prepares the indicated buffer for retained-mode rendering.
+ */
+BufferContext *GraphicsStateGuardian::
+prepare_shader_buffer(ShaderBuffer *) {
+  return (BufferContext *)NULL;
+}
+
+/**
+ * Frees the resources previously allocated via a call to prepare_data(),
+ * including deleting the BufferContext itself, if necessary.
+ */
+void GraphicsStateGuardian::
+release_shader_buffer(BufferContext *) {
+}
+
 /**
 /**
  * Begins a new occlusion query.  After this call, you may call
  * Begins a new occlusion query.  After this call, you may call
  * begin_draw_primitives() and draw_triangles()/draw_whatever() repeatedly.
  * begin_draw_primitives() and draw_triangles()/draw_whatever() repeatedly.
@@ -1363,8 +1387,18 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       }
       }
     }
     }
 
 
-    // TODO: dummy light
-    nassertr(false, &LMatrix4::ident_mat());
+    // Apply the default OpenGL lights otherwise.
+    // Special exception for light 0, which defaults to white.
+    if (index == 0) {
+      string basename = name->get_basename();
+      if (basename == "color" || basename == "diffuse") {
+        t.set_row(3, _light_color_scale);
+        return &t;
+      } else if (basename == "specular") {
+        return &LMatrix4::ones_mat();
+      }
+    }
+    return fetch_specified_member(NodePath(), name, t);
   }
   }
   default:
   default:
     nassertr(false /*should never get here*/, &LMatrix4::ident_mat());
     nassertr(false /*should never get here*/, &LMatrix4::ident_mat());
@@ -1395,8 +1429,16 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
   static const CPT_InternalName IN_quadraticAttenuation("quadraticAttenuation");
   static const CPT_InternalName IN_quadraticAttenuation("quadraticAttenuation");
   static const CPT_InternalName IN_shadowMatrix("shadowMatrix");
   static const CPT_InternalName IN_shadowMatrix("shadowMatrix");
 
 
+  PandaNode *node = NULL;
+  if (!np.is_empty()) {
+    node = np.node();
+  }
+
   if (attrib == IN_color) {
   if (attrib == IN_color) {
-    Light *light = np.node()->as_light();
+    if (node == (PandaNode *)NULL) {
+      return &LMatrix4::ident_mat();
+    }
+    Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
     LColor c = light->get_color();
     LColor c = light->get_color();
     c.componentwise_mult(_light_color_scale);
     c.componentwise_mult(_light_color_scale);
@@ -1404,9 +1446,12 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     return &t;
     return &t;
 
 
   } else if (attrib == IN_ambient) {
   } else if (attrib == IN_ambient) {
-    Light *light = np.node()->as_light();
+    if (node == (PandaNode *)NULL) {
+      return &LMatrix4::ident_mat();
+    }
+    Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
-    if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+    if (node->is_of_type(AmbientLight::get_class_type())) {
       LColor c = light->get_color();
       LColor c = light->get_color();
       c.componentwise_mult(_light_color_scale);
       c.componentwise_mult(_light_color_scale);
       t.set_row(3, c);
       t.set_row(3, c);
@@ -1417,9 +1462,12 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     return &t;
     return &t;
 
 
   } else if (attrib == IN_diffuse) {
   } else if (attrib == IN_diffuse) {
-    Light *light = np.node()->as_light();
+    if (node == (PandaNode *)NULL) {
+      return &LMatrix4::ident_mat();
+    }
+    Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
     nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
-    if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+    if (node->is_of_type(AmbientLight::get_class_type())) {
       // Ambient light has no diffuse color.
       // Ambient light has no diffuse color.
       t.set_row(3, LColor(0.0f, 0.0f, 0.0f, 1.0f));
       t.set_row(3, LColor(0.0f, 0.0f, 0.0f, 1.0f));
     } else {
     } else {
@@ -1430,19 +1478,25 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     return &t;
     return &t;
 
 
   } else if (attrib == IN_specular) {
   } else if (attrib == IN_specular) {
-    Light *light = np.node()->as_light();
+    if (node == (PandaNode *)NULL) {
+      return &LMatrix4::ident_mat();
+    }
+    Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
     nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
     t.set_row(3, light->get_specular_color());
     t.set_row(3, light->get_specular_color());
     return &t;
     return &t;
 
 
   } else if (attrib == IN_position) {
   } else if (attrib == IN_position) {
-    if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+    if (np.is_empty()) {
+      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0);
+      return &t;
+    } else if (node->is_of_type(AmbientLight::get_class_type())) {
       // Ambient light has no position.
       // Ambient light has no position.
       t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
       t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
       return &t;
       return &t;
-    } else if (np.node()->is_of_type(DirectionalLight::get_class_type())) {
+    } else if (node->is_of_type(DirectionalLight::get_class_type())) {
       DirectionalLight *light;
       DirectionalLight *light;
-      DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
+      DCAST_INTO_R(light, node, &LMatrix4::ident_mat());
 
 
       CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
       CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
       LVector3 dir = -(light->get_direction() * transform->get_mat());
       LVector3 dir = -(light->get_direction() * transform->get_mat());
@@ -1451,7 +1505,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
       return &t;
       return &t;
     } else {
     } else {
       LightLensNode *light;
       LightLensNode *light;
-      DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
+      DCAST_INTO_R(light, node, &LMatrix4::ident_mat());
       Lens *lens = light->get_lens();
       Lens *lens = light->get_lens();
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
 
 
@@ -1466,13 +1520,16 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     }
     }
 
 
   } else if (attrib == IN_halfVector) {
   } else if (attrib == IN_halfVector) {
-    if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+    if (np.is_empty()) {
+      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0);
+      return &t;
+    } else if (node->is_of_type(AmbientLight::get_class_type())) {
       // Ambient light has no half-vector.
       // Ambient light has no half-vector.
       t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
       t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
       return &t;
       return &t;
-    } else if (np.node()->is_of_type(DirectionalLight::get_class_type())) {
+    } else if (node->is_of_type(DirectionalLight::get_class_type())) {
       DirectionalLight *light;
       DirectionalLight *light;
-      DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
+      DCAST_INTO_R(light, node, &LMatrix4::ident_mat());
 
 
       CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
       CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
       LVector3 dir = -(light->get_direction() * transform->get_mat());
       LVector3 dir = -(light->get_direction() * transform->get_mat());
@@ -1484,7 +1541,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
       return &t;
       return &t;
     } else {
     } else {
       LightLensNode *light;
       LightLensNode *light;
-      DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
+      DCAST_INTO_R(light, node, &LMatrix4::ident_mat());
       Lens *lens = light->get_lens();
       Lens *lens = light->get_lens();
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
 
 
@@ -1502,13 +1559,16 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     }
     }
 
 
   } else if (attrib == IN_spotDirection) {
   } else if (attrib == IN_spotDirection) {
-    if (np.node()->is_of_type(AmbientLight::get_class_type())) {
+    if (node == (PandaNode *)NULL) {
+      t.set_row(3, LVector3(0.0f, 0.0f, -1.0f));
+      return &t;
+    } else if (node->is_of_type(AmbientLight::get_class_type())) {
       // Ambient light has no spot direction.
       // Ambient light has no spot direction.
       t.set_row(3, LVector3(0.0f, 0.0f, 0.0f));
       t.set_row(3, LVector3(0.0f, 0.0f, 0.0f));
       return &t;
       return &t;
     } else {
     } else {
       LightLensNode *light;
       LightLensNode *light;
-      DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
+      DCAST_INTO_R(light, node, &LMatrix4::ident_mat());
       Lens *lens = light->get_lens();
       Lens *lens = light->get_lens();
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
 
 
@@ -1523,9 +1583,10 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     }
     }
 
 
   } else if (attrib == IN_spotCutoff) {
   } else if (attrib == IN_spotCutoff) {
-    if (np.node()->is_of_type(Spotlight::get_class_type())) {
+    if (node != (PandaNode *)NULL &&
+        node->is_of_type(Spotlight::get_class_type())) {
       LightLensNode *light;
       LightLensNode *light;
-      DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
+      DCAST_INTO_R(light, node, &LMatrix4::ident_mat());
       Lens *lens = light->get_lens();
       Lens *lens = light->get_lens();
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
 
 
@@ -1539,9 +1600,10 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     }
     }
 
 
   } else if (attrib == IN_spotCosCutoff) {
   } else if (attrib == IN_spotCosCutoff) {
-    if (np.node()->is_of_type(Spotlight::get_class_type())) {
+    if (node != (PandaNode *)NULL &&
+        node->is_of_type(Spotlight::get_class_type())) {
       LightLensNode *light;
       LightLensNode *light;
-      DCAST_INTO_R(light, np.node(), &LMatrix4::ident_mat());
+      DCAST_INTO_R(light, node, &LMatrix4::ident_mat());
       Lens *lens = light->get_lens();
       Lens *lens = light->get_lens();
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
 
 
@@ -1553,51 +1615,72 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
       t.set_row(3, LVecBase4(-1));
       t.set_row(3, LVecBase4(-1));
       return &t;
       return &t;
     }
     }
+
   } else if (attrib == IN_spotExponent) {
   } else if (attrib == IN_spotExponent) {
-    Light *light = np.node()->as_light();
+    if (node == (PandaNode *)NULL) {
+      return &LMatrix4::zeros_mat();
+    }
+    Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
 
 
     t.set_row(3, LVecBase4(light->get_exponent()));
     t.set_row(3, LVecBase4(light->get_exponent()));
     return &t;
     return &t;
 
 
   } else if (attrib == IN_attenuation) {
   } else if (attrib == IN_attenuation) {
-    Light *light = np.node()->as_light();
-    nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
+    if (node != (PandaNode *)NULL) {
+      Light *light = node->as_light();
+      nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
 
 
-    t.set_row(3, LVecBase4(light->get_attenuation(), 0));
+      t.set_row(3, LVecBase4(light->get_attenuation(), 0));
+    } else {
+      t.set_row(3, LVecBase4(1, 0, 0, 0));
+    }
     return &t;
     return &t;
 
 
   } else if (attrib == IN_constantAttenuation) {
   } else if (attrib == IN_constantAttenuation) {
-    Light *light = np.node()->as_light();
+    if (node == (PandaNode *)NULL) {
+      return &LMatrix4::ones_mat();
+    }
+    Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
     nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
 
 
     t.set_row(3, LVecBase4(light->get_attenuation()[0]));
     t.set_row(3, LVecBase4(light->get_attenuation()[0]));
     return &t;
     return &t;
 
 
   } else if (attrib == IN_linearAttenuation) {
   } else if (attrib == IN_linearAttenuation) {
-    Light *light = np.node()->as_light();
+    if (node == (PandaNode *)NULL) {
+      return &LMatrix4::zeros_mat();
+    }
+    Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
 
 
     t.set_row(3, LVecBase4(light->get_attenuation()[1]));
     t.set_row(3, LVecBase4(light->get_attenuation()[1]));
     return &t;
     return &t;
 
 
   } else if (attrib == IN_quadraticAttenuation) {
   } else if (attrib == IN_quadraticAttenuation) {
-    Light *light = np.node()->as_light();
+    if (node == (PandaNode *)NULL) {
+      return &LMatrix4::zeros_mat();
+    }
+    Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
 
 
     t.set_row(3, LVecBase4(light->get_attenuation()[2]));
     t.set_row(3, LVecBase4(light->get_attenuation()[2]));
     return &t;
     return &t;
 
 
   } else if (attrib == IN_shadowMatrix) {
   } else if (attrib == IN_shadowMatrix) {
-    LensNode *lnode;
-    DCAST_INTO_R(lnode, np.node(), &LMatrix4::ident_mat());
-    Lens *lens = lnode->get_lens();
-
     static const LMatrix4 biasmat(0.5f, 0.0f, 0.0f, 0.0f,
     static const LMatrix4 biasmat(0.5f, 0.0f, 0.0f, 0.0f,
                                   0.0f, 0.5f, 0.0f, 0.0f,
                                   0.0f, 0.5f, 0.0f, 0.0f,
                                   0.0f, 0.0f, 0.5f, 0.0f,
                                   0.0f, 0.0f, 0.5f, 0.0f,
                                   0.5f, 0.5f, 0.5f, 1.0f);
                                   0.5f, 0.5f, 0.5f, 1.0f);
 
 
+    if (node == (PandaNode *)NULL) {
+      return &biasmat;
+    }
+
+    LensNode *lnode;
+    DCAST_INTO_R(lnode, node, &LMatrix4::ident_mat());
+    Lens *lens = lnode->get_lens();
+
     t = get_external_transform()->get_mat() *
     t = get_external_transform()->get_mat() *
       get_scene()->get_camera_transform()->get_mat() *
       get_scene()->get_camera_transform()->get_mat() *
       np.get_net_transform()->get_inverse()->get_mat() *
       np.get_net_transform()->get_inverse()->get_mat() *

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

@@ -88,6 +88,7 @@ PUBLISHED:
   INLINE int release_all_geoms();
   INLINE int release_all_geoms();
   INLINE int release_all_vertex_buffers();
   INLINE int release_all_vertex_buffers();
   INLINE int release_all_index_buffers();
   INLINE int release_all_index_buffers();
+  INLINE int release_all_shader_buffers();
 
 
   INLINE void set_active(bool active);
   INLINE void set_active(bool active);
   INLINE bool is_active() const;
   INLINE bool is_active() const;
@@ -307,6 +308,9 @@ public:
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
   virtual void release_index_buffer(IndexBufferContext *ibc);
   virtual void release_index_buffer(IndexBufferContext *ibc);
 
 
+  virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data);
+  virtual void release_shader_buffer(BufferContext *ibc);
+
   virtual void begin_occlusion_query();
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
 
 
@@ -322,7 +326,7 @@ public:
   virtual void set_state_and_transform(const RenderState *state,
   virtual void set_state_and_transform(const RenderState *state,
                                        const TransformState *transform);
                                        const TransformState *transform);
 
 
-  virtual PN_stdfloat compute_distance_to(const LPoint3 &point) const;
+  PN_stdfloat compute_distance_to(const LPoint3 &point) const;
 
 
   virtual void clear(DrawableRegion *clearable);
   virtual void clear(DrawableRegion *clearable);
 
 
@@ -640,10 +644,13 @@ public:
   // Statistics
   // Statistics
   static PStatCollector _vertex_buffer_switch_pcollector;
   static PStatCollector _vertex_buffer_switch_pcollector;
   static PStatCollector _index_buffer_switch_pcollector;
   static PStatCollector _index_buffer_switch_pcollector;
+  static PStatCollector _shader_buffer_switch_pcollector;
   static PStatCollector _load_vertex_buffer_pcollector;
   static PStatCollector _load_vertex_buffer_pcollector;
   static PStatCollector _load_index_buffer_pcollector;
   static PStatCollector _load_index_buffer_pcollector;
+  static PStatCollector _load_shader_buffer_pcollector;
   static PStatCollector _create_vertex_buffer_pcollector;
   static PStatCollector _create_vertex_buffer_pcollector;
   static PStatCollector _create_index_buffer_pcollector;
   static PStatCollector _create_index_buffer_pcollector;
+  static PStatCollector _create_shader_buffer_pcollector;
   static PStatCollector _load_texture_pcollector;
   static PStatCollector _load_texture_pcollector;
   static PStatCollector _data_transferred_pcollector;
   static PStatCollector _data_transferred_pcollector;
   static PStatCollector _texmgrmem_total_pcollector;
   static PStatCollector _texmgrmem_total_pcollector;
@@ -680,6 +687,7 @@ public:
   static PStatCollector _prepare_shader_pcollector;
   static PStatCollector _prepare_shader_pcollector;
   static PStatCollector _prepare_vertex_buffer_pcollector;
   static PStatCollector _prepare_vertex_buffer_pcollector;
   static PStatCollector _prepare_index_buffer_pcollector;
   static PStatCollector _prepare_index_buffer_pcollector;
+  static PStatCollector _prepare_shader_buffer_pcollector;
 
 
   // A whole slew of collectors to measure the cost of individual state
   // A whole slew of collectors to measure the cost of individual state
   // changes.  These are disabled by default.
   // changes.  These are disabled by default.

+ 17 - 7
panda/src/downloader/httpClient.cxx

@@ -231,11 +231,13 @@ operator = (const HTTPClient &copy) {
  */
  */
 HTTPClient::
 HTTPClient::
 ~HTTPClient() {
 ~HTTPClient() {
-  // Before we can free the context, we must remove the X509_STORE pointer
-  // from it, so it won't be destroyed along with it (this object is shared
-  // among all contexts).
   if (_ssl_ctx != (SSL_CTX *)NULL) {
   if (_ssl_ctx != (SSL_CTX *)NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+    // Before we can free the context, we must remove the X509_STORE pointer
+    // from it, so it won't be destroyed along with it (this object is shared
+    // among all contexts).
     _ssl_ctx->cert_store = NULL;
     _ssl_ctx->cert_store = NULL;
+#endif
     SSL_CTX_free(_ssl_ctx);
     SSL_CTX_free(_ssl_ctx);
   }
   }
 
 
@@ -1122,7 +1124,13 @@ get_ssl_ctx() {
   OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
   OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
   sslw->notify_ssl_errors();
   sslw->notify_ssl_errors();
 
 
-  SSL_CTX_set_cert_store(_ssl_ctx, sslw->get_x509_store());
+  X509_STORE *store = sslw->get_x509_store();
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+  if (store != NULL) {
+    X509_STORE_up_ref(store);
+  }
+#endif
+  SSL_CTX_set_cert_store(_ssl_ctx, store);
 
 
   return _ssl_ctx;
   return _ssl_ctx;
 }
 }
@@ -1511,15 +1519,17 @@ x509_name_subset(X509_NAME *name_a, X509_NAME *name_b) {
   for (int ai = 0; ai < count_a; ai++) {
   for (int ai = 0; ai < count_a; ai++) {
     X509_NAME_ENTRY *na = X509_NAME_get_entry(name_a, ai);
     X509_NAME_ENTRY *na = X509_NAME_get_entry(name_a, ai);
 
 
-    int bi = X509_NAME_get_index_by_OBJ(name_b, na->object, -1);
+    int bi = X509_NAME_get_index_by_OBJ(name_b, X509_NAME_ENTRY_get_object(na), -1);
     if (bi < 0) {
     if (bi < 0) {
       // This entry in name_a is not defined in name_b.
       // This entry in name_a is not defined in name_b.
       return false;
       return false;
     }
     }
 
 
     X509_NAME_ENTRY *nb = X509_NAME_get_entry(name_b, bi);
     X509_NAME_ENTRY *nb = X509_NAME_get_entry(name_b, bi);
-    if (na->value->length != nb->value->length ||
-        memcmp(na->value->data, nb->value->data, na->value->length) != 0) {
+    ASN1_STRING *na_value = X509_NAME_ENTRY_get_data(na);
+    ASN1_STRING *nb_value = X509_NAME_ENTRY_get_data(nb);
+    if (na_value->length != nb_value->length ||
+        memcmp(na_value->data, nb_value->data, na_value->length) != 0) {
       // This entry in name_a doesn't match that of name_b.
       // This entry in name_a doesn't match that of name_b.
       return false;
       return false;
     }
     }

+ 18 - 2
panda/src/dxgsg9/wdxGraphicsPipe9.cxx

@@ -19,6 +19,16 @@
 
 
 TypeHandle wdxGraphicsPipe9::_type_handle;
 TypeHandle wdxGraphicsPipe9::_type_handle;
 
 
+static bool MyGetProcAddr(HINSTANCE hDLL, FARPROC *pFn, const char *szExportedFnName) {
+  *pFn = (FARPROC) GetProcAddress(hDLL, szExportedFnName);
+  if (*pFn == NULL) {
+    wdxdisplay9_cat.error()
+      << "GetProcAddr failed for " << szExportedFnName << ", error=" << GetLastError() <<endl;
+    return false;
+  }
+  return true;
+}
+
 #define LOWVIDMEMTHRESHOLD 5700000  // 4MB cards should fall below this
 #define LOWVIDMEMTHRESHOLD 5700000  // 4MB cards should fall below this
 #define CRAPPY_DRIVER_IS_LYING_VIDMEMTHRESHOLD 1000000  // if # is > 1MB, card is lying and I cant tell what it is
 #define CRAPPY_DRIVER_IS_LYING_VIDMEMTHRESHOLD 1000000  // if # is > 1MB, card is lying and I cant tell what it is
 #define UNKNOWN_VIDMEM_SIZE 0xFFFFFFFF
 #define UNKNOWN_VIDMEM_SIZE 0xFFFFFFFF
@@ -154,7 +164,10 @@ make_output(const string &name,
  */
  */
 bool wdxGraphicsPipe9::
 bool wdxGraphicsPipe9::
 init() {
 init() {
-  if (!MyLoadLib(_hDDrawDLL, "ddraw.dll")) {
+  _hDDrawDLL = LoadLibrary("ddraw.dll");
+  if (_hDDrawDLL == NULL) {
+    wdxdisplay9_cat.error()
+      << "LoadLibrary failed for ddraw.dll, error=" << GetLastError() <<endl;
     goto error;
     goto error;
   }
   }
 
 
@@ -166,7 +179,10 @@ init() {
     goto error;
     goto error;
   }
   }
 
 
-  if (!MyLoadLib(_hD3D9_DLL, "d3d9.dll")) {
+  _hD3D9_DLL = LoadLibrary("d3d9.dll");
+  if (_hD3D9_DLL == NULL) {
+    wdxdisplay9_cat.error()
+      << "LoadLibrary failed for d3d9.dll, error=" << GetLastError() <<endl;
     goto error;
     goto error;
   }
   }
 
 

+ 1 - 0
panda/src/egg2pg/save_egg_file.cxx

@@ -14,6 +14,7 @@
 #include "save_egg_file.h"
 #include "save_egg_file.h"
 #include "eggSaver.h"
 #include "eggSaver.h"
 #include "config_egg2pg.h"
 #include "config_egg2pg.h"
+#include "modelRoot.h"
 #include "sceneGraphReducer.h"
 #include "sceneGraphReducer.h"
 #include "virtualFileSystem.h"
 #include "virtualFileSystem.h"
 #include "config_util.h"
 #include "config_util.h"

+ 119 - 0
panda/src/express/filename_ext.cxx

@@ -14,6 +14,115 @@
 #include "filename_ext.h"
 #include "filename_ext.h"
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
+
+#ifndef CPPPARSER
+extern Dtool_PyTypedObject Dtool_Filename;
+#endif  // CPPPARSER
+
+/**
+ * Constructs a Filename object from a str, bytes object, or os.PathLike.
+ */
+void Extension<Filename>::
+__init__(PyObject *path) {
+  nassertv(path != NULL);
+  nassertv(_this != NULL);
+
+  Py_ssize_t length;
+
+  if (PyUnicode_CheckExact(path)) {
+    wchar_t *data;
+#if PY_VERSION_HEX >= 0x03020000
+    data = PyUnicode_AsWideCharString(path, &length);
+#else
+    length = PyUnicode_GET_SIZE(path);
+    data = (wchar_t *)alloca(sizeof(wchar_t) * (length + 1));
+    PyUnicode_AsWideChar((PyUnicodeObject *)path, data, length);
+#endif
+    (*_this) = wstring(data, length);
+
+#if PY_VERSION_HEX >= 0x03020000
+    PyMem_Free(data);
+#endif
+    return;
+  }
+
+  if (PyBytes_CheckExact(path)) {
+    char *data;
+    PyBytes_AsStringAndSize(path, &data, &length);
+    (*_this) = string(data, length);
+    return;
+  }
+
+  if (Py_TYPE(path) == &Dtool_Filename._PyType) {
+    // Copy constructor.
+    (*_this) = *((Filename *)((Dtool_PyInstDef *)path)->_ptr_to_object);
+    return;
+  }
+
+  PyObject *path_str;
+
+#if PY_VERSION_HEX >= 0x03060000
+  // It must be an os.PathLike object.  Check for an __fspath__ method.
+  PyObject *fspath = PyObject_GetAttrString((PyObject *)Py_TYPE(path), "__fspath__");
+  if (fspath == NULL) {
+    PyErr_Format(PyExc_TypeError, "expected str, bytes or os.PathLike object, not %s", Py_TYPE(path)->tp_name);
+    return;
+  }
+
+  path_str = PyObject_CallFunctionObjArgs(fspath, path, NULL);
+  Py_DECREF(fspath);
+#else
+  // There is no standard path protocol before Python 3.6, but let's try and
+  // support taking pathlib paths anyway.  We don't version check this to
+  // allow people to use backports of the pathlib module.
+  if (PyObject_HasAttrString(path, "_format_parsed_parts")) {
+    path_str = PyObject_Str(path);
+  } else {
+#if PY_VERSION_HEX >= 0x03040000
+    PyErr_Format(PyExc_TypeError, "expected str, bytes, Path or Filename object, not %s", Py_TYPE(path)->tp_name);
+#elif PY_MAJOR_VERSION >= 3
+    PyErr_Format(PyExc_TypeError, "expected str, bytes or Filename object, not %s", Py_TYPE(path)->tp_name);
+#else
+    PyErr_Format(PyExc_TypeError, "expected str or unicode object, not %s", Py_TYPE(path)->tp_name);
+#endif
+    return;
+  }
+#endif
+
+  if (path_str == NULL) {
+    return;
+  }
+
+  if (PyUnicode_CheckExact(path_str)) {
+    wchar_t *data;
+#if PY_VERSION_HEX >= 0x03020000
+    data = PyUnicode_AsWideCharString(path_str, &length);
+#else
+    length = PyUnicode_GET_SIZE(path_str);
+    data = (wchar_t *)alloca(sizeof(wchar_t) * (length + 1));
+    PyUnicode_AsWideChar((PyUnicodeObject *)path_str, data, length);
+#endif
+    (*_this) = Filename::from_os_specific_w(wstring(data, length));
+
+#if PY_VERSION_HEX >= 0x03020000
+    PyMem_Free(data);
+#endif
+
+  } else if (PyBytes_CheckExact(path_str)) {
+    char *data;
+    PyBytes_AsStringAndSize(path_str, &data, &length);
+    (*_this) = Filename::from_os_specific(string(data, length));
+
+  } else {
+#if PY_MAJOR_VERSION >= 3
+    PyErr_Format(PyExc_TypeError, "expected str or bytes object, not %s", Py_TYPE(path_str)->tp_name);
+#else
+    PyErr_Format(PyExc_TypeError, "expected str or unicode object, not %s", Py_TYPE(path_str)->tp_name);
+#endif
+  }
+  Py_DECREF(path_str);
+}
+
 /**
 /**
  * This special Python method is implement to provide support for the pickle
  * This special Python method is implement to provide support for the pickle
  * module.
  * module.
@@ -62,6 +171,16 @@ __repr__() const {
   return result;
   return result;
 }
 }
 
 
+/**
+ * Allows a Filename object to be passed to any Python function that accepts
+ * an os.PathLike object.
+ */
+PyObject *Extension<Filename>::
+__fspath__() const {
+  wstring filename = _this->to_os_specific_w();
+  return PyUnicode_FromWideChar(filename.data(), (Py_ssize_t)filename.size());
+}
+
 /**
 /**
  * This variant on scan_directory returns a Python list of strings on success,
  * This variant on scan_directory returns a Python list of strings on success,
  * or None on failure.
  * or None on failure.

+ 3 - 0
panda/src/express/filename_ext.h

@@ -29,8 +29,11 @@
 template<>
 template<>
 class Extension<Filename> : public ExtensionBase<Filename> {
 class Extension<Filename> : public ExtensionBase<Filename> {
 public:
 public:
+  void __init__(PyObject *path);
+
   PyObject *__reduce__(PyObject *self) const;
   PyObject *__reduce__(PyObject *self) const;
   PyObject *__repr__() const;
   PyObject *__repr__() const;
+  PyObject *__fspath__() const;
   PyObject *scan_directory() const;
   PyObject *scan_directory() const;
 };
 };
 
 

+ 8 - 0
panda/src/ffmpeg/config_ffmpeg.cxx

@@ -76,6 +76,14 @@ ConfigVariableInt ffmpeg_read_buffer_size
           "This is important for performance.  A typical size is that of a "
           "This is important for performance.  A typical size is that of a "
           "cache page, e.g. 4kb."));
           "cache page, e.g. 4kb."));
 
 
+ConfigVariableBool ffmpeg_prefer_libvpx
+("ffmpeg-prefer-libvpx", false,
+ PRC_DESC("If this is true, Panda will overrule ffmpeg's best judgment on "
+          "which decoder to use for decoding VP8 and VP9 files, and try to "
+          "choose libvpx.  This is useful when you want to play WebM videos "
+          "with an alpha channel, which aren't supported by ffmpeg's own "
+          "VP8/VP9 decoders."));
+
 /**
 /**
  * Initializes the library.  This must be called at least once before any of
  * Initializes the library.  This must be called at least once before any of
  * the functions or classes in this library can be used.  Normally it will be
  * the functions or classes in this library can be used.  Normally it will be

+ 1 - 0
panda/src/ffmpeg/config_ffmpeg.h

@@ -31,6 +31,7 @@ extern ConfigVariableBool ffmpeg_support_seek;
 extern ConfigVariableBool ffmpeg_global_lock;
 extern ConfigVariableBool ffmpeg_global_lock;
 extern ConfigVariableEnum<ThreadPriority> ffmpeg_thread_priority;
 extern ConfigVariableEnum<ThreadPriority> ffmpeg_thread_priority;
 extern ConfigVariableInt ffmpeg_read_buffer_size;
 extern ConfigVariableInt ffmpeg_read_buffer_size;
+extern ConfigVariableBool ffmpeg_prefer_libvpx;
 
 
 extern EXPCL_FFMPEG void init_libffmpeg();
 extern EXPCL_FFMPEG void init_libffmpeg();
 
 

+ 0 - 2
panda/src/ffmpeg/ffmpegAudioCursor.cxx

@@ -50,9 +50,7 @@ FfmpegAudioCursor(FfmpegAudio *src) :
   _packet_data(0),
   _packet_data(0),
   _format_ctx(0),
   _format_ctx(0),
   _audio_ctx(0),
   _audio_ctx(0),
-#ifdef HAVE_SWRESAMPLE
   _resample_ctx(0),
   _resample_ctx(0),
-#endif
   _buffer(0),
   _buffer(0),
   _buffer_alloc(0),
   _buffer_alloc(0),
   _frame(0)
   _frame(0)

+ 0 - 5
panda/src/ffmpeg/ffmpegAudioCursor.h

@@ -31,10 +31,7 @@ struct AVFormatContext;
 struct AVCodecContext;
 struct AVCodecContext;
 struct AVStream;
 struct AVStream;
 struct AVPacket;
 struct AVPacket;
-
-#ifdef HAVE_SWRESAMPLE
 struct SwrContext;
 struct SwrContext;
-#endif
 
 
 /**
 /**
  * A stream that generates a sequence of audio samples.
  * A stream that generates a sequence of audio samples.
@@ -72,9 +69,7 @@ protected:
   int       _buffer_head;
   int       _buffer_head;
   int       _buffer_tail;
   int       _buffer_tail;
 
 
-#ifdef HAVE_SWRESAMPLE
   SwrContext *_resample_ctx;
   SwrContext *_resample_ctx;
-#endif
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 48 - 18
panda/src/ffmpeg/ffmpegVideoCursor.cxx

@@ -22,6 +22,7 @@
 extern "C" {
 extern "C" {
   #include "libavcodec/avcodec.h"
   #include "libavcodec/avcodec.h"
   #include "libavformat/avformat.h"
   #include "libavformat/avformat.h"
+  #include "libavutil/pixdesc.h"
 #ifdef HAVE_SWSCALE
 #ifdef HAVE_SWSCALE
   #include "libswscale/swscale.h"
   #include "libswscale/swscale.h"
 #endif
 #endif
@@ -35,11 +36,21 @@ PStatCollector FfmpegVideoCursor::_fetch_buffer_pcollector("*:FFMPEG Video Decod
 PStatCollector FfmpegVideoCursor::_seek_pcollector("*:FFMPEG Video Decoding:Seek");
 PStatCollector FfmpegVideoCursor::_seek_pcollector("*:FFMPEG Video Decoding:Seek");
 PStatCollector FfmpegVideoCursor::_export_frame_pcollector("*:FFMPEG Convert Video to BGR");
 PStatCollector FfmpegVideoCursor::_export_frame_pcollector("*:FFMPEG Convert Video to BGR");
 
 
-
 #if LIBAVFORMAT_VERSION_MAJOR < 53
 #if LIBAVFORMAT_VERSION_MAJOR < 53
   #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
   #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
 #endif
 #endif
 
 
+#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 74, 100)
+#define AV_PIX_FMT_NONE PIX_FMT_NONE
+#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
+#define AV_PIX_FMT_BGRA PIX_FMT_BGRA
+typedef PixelFormat AVPixelFormat;
+#endif
+
+#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 32, 100)
+#define AV_PIX_FMT_FLAG_ALPHA PIX_FMT_ALPHA
+#endif
+
 /**
 /**
  * This constructor is only used when reading from a bam file.
  * This constructor is only used when reading from a bam file.
  */
  */
@@ -55,6 +66,7 @@ FfmpegVideoCursor() :
   _format_ctx(NULL),
   _format_ctx(NULL),
   _video_ctx(NULL),
   _video_ctx(NULL),
   _convert_ctx(NULL),
   _convert_ctx(NULL),
+  _pixel_format((int)AV_PIX_FMT_NONE),
   _video_index(-1),
   _video_index(-1),
   _frame(NULL),
   _frame(NULL),
   _frame_out(NULL),
   _frame_out(NULL),
@@ -80,17 +92,6 @@ init_from(FfmpegVideo *source) {
 
 
   ReMutexHolder av_holder(_av_lock);
   ReMutexHolder av_holder(_av_lock);
 
 
-#ifdef HAVE_SWSCALE
-  nassertv(_convert_ctx == NULL);
-  _convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt,
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 74, 100)
-                                _size_x, _size_y, AV_PIX_FMT_BGR24,
-#else
-                                _size_x, _size_y, PIX_FMT_BGR24,
-#endif
-                                SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
-#endif  // HAVE_SWSCALE
-
 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
   _frame = av_frame_alloc();
   _frame = av_frame_alloc();
   _frame_out = av_frame_alloc();
   _frame_out = av_frame_alloc();
@@ -115,6 +116,25 @@ init_from(FfmpegVideo *source) {
   _eof_known = false;
   _eof_known = false;
   _eof_frame = 0;
   _eof_frame = 0;
 
 
+  // Check if we got an alpha format.  Please note that some video codecs
+  // (eg. libvpx) change the pix_fmt after decoding the first frame, which is
+  // why we didn't do this earlier.
+  const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(_video_ctx->pix_fmt);
+  if (desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA) != 0) {
+    _num_components = 4;
+    _pixel_format = (int)AV_PIX_FMT_BGRA;
+  } else {
+    _num_components = 3;
+    _pixel_format = (int)AV_PIX_FMT_BGR24;
+  }
+
+#ifdef HAVE_SWSCALE
+  nassertv(_convert_ctx == NULL);
+  _convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt,
+                                _size_x, _size_y, (AVPixelFormat)_pixel_format,
+                                SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
+#endif  // HAVE_SWSCALE
+
 #ifdef HAVE_THREADS
 #ifdef HAVE_THREADS
   set_max_readahead_frames(ffmpeg_max_readahead_frames);
   set_max_readahead_frames(ffmpeg_max_readahead_frames);
 #endif  // HAVE_THREADS
 #endif  // HAVE_THREADS
@@ -495,7 +515,17 @@ open_stream() {
     return false;
     return false;
   }
   }
 
 
-  AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
+  AVCodec *pVideoCodec = NULL;
+  if (ffmpeg_prefer_libvpx) {
+    if ((int)_video_ctx->codec_id == 168) { // AV_CODEC_ID_VP9
+      pVideoCodec = avcodec_find_decoder_by_name("libvpx-vp9");
+    } else if (_video_ctx->codec_id == AV_CODEC_ID_VP8) {
+      pVideoCodec = avcodec_find_decoder_by_name("libvpx");
+    }
+  }
+  if (pVideoCodec == NULL) {
+    pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id);
+  }
   if (pVideoCodec == NULL) {
   if (pVideoCodec == NULL) {
     ffmpeg_cat.info()
     ffmpeg_cat.info()
       << "Couldn't find codec\n";
       << "Couldn't find codec\n";
@@ -515,7 +545,7 @@ open_stream() {
 
 
   _size_x = _video_ctx->width;
   _size_x = _video_ctx->width;
   _size_y = _video_ctx->height;
   _size_y = _video_ctx->height;
-  _num_components = 3; // Don't know how to implement RGBA movies yet.
+  _num_components = 3;
   _length = (double)_format_ctx->duration / (double)AV_TIME_BASE;
   _length = (double)_format_ctx->duration / (double)AV_TIME_BASE;
   _can_seek = true;
   _can_seek = true;
   _can_seek_fast = true;
   _can_seek_fast = true;
@@ -1075,8 +1105,8 @@ export_frame(FfmpegBuffer *buffer) {
     return;
     return;
   }
   }
 
 
-  _frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * 3);
-  _frame_out->linesize[0] = _size_x * -3;
+  _frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * _num_components);
+  _frame_out->linesize[0] = _size_x * -_num_components;
   buffer->_begin_frame = _begin_frame;
   buffer->_begin_frame = _begin_frame;
   buffer->_end_frame = _end_frame;
   buffer->_end_frame = _end_frame;
 
 
@@ -1086,7 +1116,7 @@ export_frame(FfmpegBuffer *buffer) {
     nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
     nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
     sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
     sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
 #else
 #else
-    img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
+    img_convert((AVPicture *)_frame_out, (AVPixelFormat)_pixel_format,
                 (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
                 (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
 #endif
 #endif
   } else {
   } else {
@@ -1094,7 +1124,7 @@ export_frame(FfmpegBuffer *buffer) {
     nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
     nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL);
     sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
     sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize);
 #else
 #else
-    img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24,
+    img_convert((AVPicture *)_frame_out, (AVPixelFormat)_pixel_format,
                 (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
                 (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
 #endif
 #endif
   }
   }

+ 2 - 0
panda/src/ffmpeg/ffmpegVideoCursor.h

@@ -105,6 +105,8 @@ private:
   ThreadPriority _thread_priority;
   ThreadPriority _thread_priority;
   PT(GenericThread) _thread;
   PT(GenericThread) _thread;
 
 
+  int _pixel_format;
+
   // This global Mutex protects calls to avcodec_opencloseetc.
   // This global Mutex protects calls to avcodec_opencloseetc.
   static ReMutex _av_lock;
   static ReMutex _av_lock;
 
 

+ 25 - 0
panda/src/glstuff/glBufferContext_src.I

@@ -0,0 +1,25 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file glBufferContext_src.I
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+/**
+ *
+ */
+INLINE CLP(BufferContext)::
+CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg,
+                   PreparedGraphicsObjects *pgo) :
+  BufferContext(&pgo->_sbuffer_residency),
+  AdaptiveLruPage(0),
+  _glgsg(glgsg)
+{
+  _index = 0;
+}

+ 49 - 0
panda/src/glstuff/glBufferContext_src.cxx

@@ -0,0 +1,49 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file glBufferContext_src.cxx
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+TypeHandle CLP(BufferContext)::_type_handle;
+
+/**
+ * Evicts the page from the LRU.  Called internally when the LRU determines
+ * that it is full.  May also be called externally when necessary to
+ * explicitly evict the page.
+ *
+ * It is legal for this method to either evict the page as requested, do
+ * nothing (in which case the eviction will be requested again at the next
+ * epoch), or requeue itself on the tail of the queue (in which case the
+ * eviction will be requested again much later).
+ */
+void CLP(BufferContext)::
+evict_lru() {
+  dequeue_lru();
+
+  // Make sure the buffer is unbound before we delete it.
+  if (_glgsg->_current_ibuffer_index == _index) {
+    if (GLCAT.is_debug() && gl_debug_buffers) {
+      GLCAT.debug()
+        << "unbinding index buffer\n";
+    }
+    _glgsg->_glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    _glgsg->_current_ibuffer_index = 0;
+  }
+
+  // Free the buffer.
+  _glgsg->_glDeleteBuffers(1, &_index);
+
+  // We still need a valid index number, though, in case we want to re-load
+  // the buffer later.
+  _glgsg->_glGenBuffers(1, &_index);
+
+  update_data_size_bytes(0);
+  set_resident(false);
+}

+ 52 - 0
panda/src/glstuff/glBufferContext_src.h

@@ -0,0 +1,52 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file glBufferContext_src.h
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+#include "pandabase.h"
+#include "bufferContext.h"
+#include "deletedChain.h"
+
+/**
+ * Caches a GeomPrimitive on the GL as a buffer object.
+ */
+class EXPCL_GL CLP(BufferContext) : public BufferContext, public AdaptiveLruPage {
+public:
+  INLINE CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg,
+                            PreparedGraphicsObjects *pgo);
+  ALLOC_DELETED_CHAIN(CLP(BufferContext));
+
+  virtual void evict_lru();
+
+  CLP(GraphicsStateGuardian) *_glgsg;
+
+  // This is the GL "name" of the data object.
+  GLuint _index;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    BufferContext::init_type();
+    register_type(_type_handle, CLASSPREFIX_QUOTED "BufferContext",
+                  BufferContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "glBufferContext_src.I"

+ 205 - 41
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -1356,6 +1356,8 @@ reset() {
       get_extension_func("glMapBuffer");
       get_extension_func("glMapBuffer");
     _glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)
     _glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)
       get_extension_func("glUnmapBuffer");
       get_extension_func("glUnmapBuffer");
+    _glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)
+      get_extension_func("glGetBufferSubData");
 #endif
 #endif
   }
   }
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
@@ -1376,6 +1378,8 @@ reset() {
       get_extension_func("glMapBufferARB");
       get_extension_func("glMapBufferARB");
     _glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)
     _glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)
       get_extension_func("glUnmapBufferARB");
       get_extension_func("glUnmapBufferARB");
+    _glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)
+      get_extension_func("glGetBufferSubDataARB");
   }
   }
 #endif  // OPENGLES_1
 #endif  // OPENGLES_1
 
 
@@ -1792,7 +1796,7 @@ reset() {
   }
   }
 #endif
 #endif
 
 
-#ifndef OPENGLES
+#ifndef OPENGLES_1
   // Check for uniform buffers.
   // Check for uniform buffers.
 #ifdef OPENGLES
 #ifdef OPENGLES
   if (is_at_least_gl_version(3, 1) || has_extension("GL_ARB_uniform_buffer_object")) {
   if (is_at_least_gl_version(3, 1) || has_extension("GL_ARB_uniform_buffer_object")) {
@@ -1806,11 +1810,29 @@ reset() {
        get_extension_func("glGetActiveUniformBlockiv");
        get_extension_func("glGetActiveUniformBlockiv");
     _glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)
     _glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)
        get_extension_func("glGetActiveUniformBlockName");
        get_extension_func("glGetActiveUniformBlockName");
+  } else {
+    _supports_uniform_buffers = false;
+  }
 
 
+#ifndef OPENGLES
+  // Check for SSBOs.
+  if (is_at_least_gl_version(4, 3) || has_extension("ARB_shader_storage_buffer_object")) {
+    _supports_shader_buffers = true;
+    _glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)
+       get_extension_func("glGetProgramInterfaceiv");
+    _glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)
+       get_extension_func("glGetProgramResourceName");
+    _glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)
+       get_extension_func("glGetProgramResourceiv");
+  } else
+#endif
+  {
+    _supports_shader_buffers = false;
+  }
+
+  if (_supports_uniform_buffers || _supports_shader_buffers) {
     _glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)
     _glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)
       get_extension_func("glBindBufferBase");
       get_extension_func("glBindBufferBase");
-  } else {
-    _supports_uniform_buffers = false;
   }
   }
 #endif
 #endif
 
 
@@ -3027,6 +3049,9 @@ reset() {
   _current_vertex_buffers.clear();
   _current_vertex_buffers.clear();
   _current_vertex_format.clear();
   _current_vertex_format.clear();
   memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32);
   memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32);
+
+  _current_sbuffer_index = 0;
+  _current_sbuffer_base.clear();
 #endif
 #endif
 
 
   report_my_gl_errors();
   report_my_gl_errors();
@@ -5869,6 +5894,122 @@ setup_primitive(const unsigned char *&client_pointer,
   return true;
   return true;
 }
 }
 
 
+#ifndef OPENGLES
+/**
+ * Creates a new retained-mode representation of the given data, and returns a
+ * newly-allocated BufferContext pointer to reference it.  It is the
+ * responsibility of the calling function to later call release_shader_buffer()
+ * with this same pointer (which will also delete the pointer).
+ *
+ * This function should not be called directly to prepare a buffer.  Instead,
+ * call ShaderBuffer::prepare().
+ */
+BufferContext *CLP(GraphicsStateGuardian)::
+prepare_shader_buffer(ShaderBuffer *data) {
+  if (_supports_shader_buffers) {
+    PStatGPUTimer timer(this, _prepare_shader_buffer_pcollector);
+
+    CLP(BufferContext) *gbc = new CLP(BufferContext)(this, _prepared_objects);
+    _glGenBuffers(1, &gbc->_index);
+
+    if (GLCAT.is_debug() && gl_debug_buffers) {
+      GLCAT.debug()
+        << "creating shader buffer " << (int)gbc->_index << ": "<< *data << "\n";
+    }
+    _glBindBuffer(GL_SHADER_STORAGE_BUFFER, gbc->_index);
+    _current_sbuffer_index = gbc->_index;
+
+    if (_use_object_labels) {
+      string name = data->get_name();
+      _glObjectLabel(GL_SHADER_STORAGE_BUFFER, gbc->_index, name.size(), name.data());
+    }
+
+    uint64_t num_bytes = data->get_data_size_bytes();
+    if (_supports_buffer_storage) {
+      _glBufferStorage(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), 0);
+    } else {
+      _glBufferData(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), get_usage(data->get_usage_hint()));
+    }
+
+    gbc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
+
+    report_my_gl_errors();
+    return gbc;
+  }
+
+  return NULL;
+}
+
+/**
+ * Binds the given shader buffer to the given binding slot.
+ */
+void CLP(GraphicsStateGuardian)::
+apply_shader_buffer(GLuint base, ShaderBuffer *buffer) {
+  GLuint index = 0;
+  if (buffer != NULL) {
+    BufferContext *bc = buffer->prepare_now(get_prepared_objects(), this);
+    if (bc != NULL) {
+      CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc);
+      index = gbc->_index;
+      gbc->set_active(true);
+    }
+  }
+
+  if (base >= _current_sbuffer_base.size()) {
+    _current_sbuffer_base.resize(base + 1, 0);
+  }
+
+  if (_current_sbuffer_base[base] != index) {
+    if (GLCAT.is_spam() && gl_debug_buffers) {
+      GLCAT.spam()
+        << "binding shader buffer " << (int)index
+        << " to index " << base << "\n";
+    }
+    _glBindBufferBase(GL_SHADER_STORAGE_BUFFER, base, index);
+    _current_sbuffer_base[base] = index;
+    _current_sbuffer_index = index;
+
+    report_my_gl_errors();
+  }
+}
+
+/**
+ * Frees the GL resources previously allocated for the data.  This function
+ * should never be called directly; instead, call Data::release() (or simply
+ * let the Data destruct).
+ */
+void CLP(GraphicsStateGuardian)::
+release_shader_buffer(BufferContext *bc) {
+  nassertv(_supports_buffers);
+
+  CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc);
+
+  if (GLCAT.is_debug() && gl_debug_buffers) {
+    GLCAT.debug()
+      << "deleting shader buffer " << (int)gbc->_index << "\n";
+  }
+
+  // Make sure the buffer is unbound before we delete it.  Not strictly
+  // necessary according to the OpenGL spec, but it might help out a flaky
+  // driver, and we need to keep our internal state consistent anyway.
+  if (_current_sbuffer_index == gbc->_index) {
+    if (GLCAT.is_spam() && gl_debug_buffers) {
+      GLCAT.spam()
+        << "unbinding shader buffer\n";
+    }
+    _glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+    _current_sbuffer_index = 0;
+  }
+
+  _glDeleteBuffers(1, &gbc->_index);
+  report_my_gl_errors();
+
+  gbc->_index = 0;
+
+  delete gbc;
+}
+#endif
+
 #ifndef OPENGLES
 #ifndef OPENGLES
 /**
 /**
  * Begins a new occlusion query.  After this call, you may call
  * Begins a new occlusion query.  After this call, you may call
@@ -6020,16 +6161,6 @@ make_geom_munger(const RenderState *state, Thread *current_thread) {
   return GeomMunger::register_munger(munger, current_thread);
   return GeomMunger::register_munger(munger, current_thread);
 }
 }
 
 
-/**
- * This function will compute the distance to the indicated point, assumed to
- * be in eye coordinates, from the camera plane.  The point is assumed to be
- * in the GSG's internal coordinate system.
- */
-PN_stdfloat CLP(GraphicsStateGuardian)::
-compute_distance_to(const LPoint3 &point) const {
-  return -point[2];
-}
-
 /**
 /**
  * Copy the pixels within the indicated display region from the framebuffer
  * Copy the pixels within the indicated display region from the framebuffer
  * into texture memory.
  * into texture memory.
@@ -6184,7 +6315,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
 
 
     if (GLCAT.is_spam()) {
     if (GLCAT.is_spam()) {
       GLCAT.spam()
       GLCAT.spam()
-        << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << ")\n";
+        << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n";
     }
     }
   }
   }
 
 
@@ -6505,7 +6636,7 @@ do_issue_shade_model() {
 
 
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
 /**
 /**
- *
+ * Called when the current ShaderAttrib state has changed.
  */
  */
 void CLP(GraphicsStateGuardian)::
 void CLP(GraphicsStateGuardian)::
 do_issue_shader() {
 do_issue_shader() {
@@ -6518,21 +6649,30 @@ do_issue_shader() {
     shader = _default_shader;
     shader = _default_shader;
     nassertv(shader != NULL);
     nassertv(shader != NULL);
   }
   }
-
 #endif
 #endif
+
   if (shader) {
   if (shader) {
-    context = shader->prepare_now(get_prepared_objects(), this);
+    if (_current_shader != shader) {
+      context = shader->prepare_now(get_prepared_objects(), this);
+    } else {
+      context = _current_shader_context;
+    }
   }
   }
+
 #ifndef SUPPORT_FIXED_FUNCTION
 #ifndef SUPPORT_FIXED_FUNCTION
   // If it failed, try applying the default shader.
   // If it failed, try applying the default shader.
   if (shader != _default_shader && (context == 0 || !context->valid())) {
   if (shader != _default_shader && (context == 0 || !context->valid())) {
     shader = _default_shader;
     shader = _default_shader;
     nassertv(shader != NULL);
     nassertv(shader != NULL);
-    context = shader->prepare_now(get_prepared_objects(), this);
+    if (_current_shader != shader) {
+      context = shader->prepare_now(get_prepared_objects(), this);
+    } else {
+      context = _current_shader_context;
+    }
   }
   }
 #endif
 #endif
 
 
-  if (context == 0 || (context->valid() == false)) {
+  if (context == 0 || !context->valid()) {
     if (_current_shader_context != 0) {
     if (_current_shader_context != 0) {
       _current_shader_context->unbind();
       _current_shader_context->unbind();
       _current_shader = 0;
       _current_shader = 0;
@@ -6544,12 +6684,16 @@ do_issue_shader() {
       // bind the new one.
       // bind the new one.
       if (_current_shader_context != NULL &&
       if (_current_shader_context != NULL &&
           _current_shader->get_language() != shader->get_language()) {
           _current_shader->get_language() != shader->get_language()) {
+        // If it's a different type of shader, make sure to unbind the old.
         _current_shader_context->unbind();
         _current_shader_context->unbind();
       }
       }
       context->bind();
       context->bind();
       _current_shader = shader;
       _current_shader = shader;
-      _current_shader_context = context;
     }
     }
+
+    // Bind the shader storage buffers.
+    context->update_shader_buffer_bindings(_current_shader_context);
+    _current_shader_context = context;
   }
   }
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES
@@ -10122,6 +10266,7 @@ set_state_and_transform(const RenderState *target,
   }
   }
 #endif
 #endif
 
 
+  // Update all of the state that is bound to the shader program.
   if (_current_shader_context != NULL) {
   if (_current_shader_context != NULL) {
     _current_shader_context->set_state_and_transform(target, transform, _projection_mat);
     _current_shader_context->set_state_and_transform(target, transform, _projection_mat);
   }
   }
@@ -11361,7 +11506,7 @@ apply_texture(CLP(TextureContext) *gtc) {
   glBindTexture(target, gtc->_index);
   glBindTexture(target, gtc->_index);
   if (GLCAT.is_spam()) {
   if (GLCAT.is_spam()) {
     GLCAT.spam()
     GLCAT.spam()
-      << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << ")\n";
+      << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *gtc->get_texture() << "\n";
   }
   }
 
 
   report_my_gl_errors();
   report_my_gl_errors();
@@ -11659,7 +11804,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
 
 
     if (GLCAT.is_spam()) {
     if (GLCAT.is_spam()) {
       GLCAT.spam()
       GLCAT.spam()
-        << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << ")\n";
+        << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n";
     }
     }
   }
   }
 
 
@@ -12632,32 +12777,38 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
   }
   }
 #endif
 #endif
 
 
+  Texture *tex = gtc->get_texture();
+
   glBindTexture(target, gtc->_index);
   glBindTexture(target, gtc->_index);
   if (GLCAT.is_spam()) {
   if (GLCAT.is_spam()) {
     GLCAT.spam()
     GLCAT.spam()
-      << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << ")\n";
+      << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n";
   }
   }
 
 
-  Texture *tex = gtc->get_texture();
-
   GLint wrap_u, wrap_v, wrap_w;
   GLint wrap_u, wrap_v, wrap_w;
   GLint minfilter, magfilter;
   GLint minfilter, magfilter;
   GLfloat border_color[4];
   GLfloat border_color[4];
 
 
-  glGetTexParameteriv(target, GL_TEXTURE_WRAP_S, &wrap_u);
-  glGetTexParameteriv(target, GL_TEXTURE_WRAP_T, &wrap_v);
-  wrap_w = GL_REPEAT;
+#ifdef OPENGLES
+  if (true) {
+#else
+  if (target != GL_TEXTURE_BUFFER) {
+#endif
+    glGetTexParameteriv(target, GL_TEXTURE_WRAP_S, &wrap_u);
+    glGetTexParameteriv(target, GL_TEXTURE_WRAP_T, &wrap_v);
+    wrap_w = GL_REPEAT;
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
-  if (_supports_3d_texture) {
-    glGetTexParameteriv(target, GL_TEXTURE_WRAP_R, &wrap_w);
-  }
+    if (_supports_3d_texture) {
+      glGetTexParameteriv(target, GL_TEXTURE_WRAP_R, &wrap_w);
+    }
 #endif
 #endif
-  glGetTexParameteriv(target, GL_TEXTURE_MIN_FILTER, &minfilter);
-  glGetTexParameteriv(target, GL_TEXTURE_MAG_FILTER, &magfilter);
+    glGetTexParameteriv(target, GL_TEXTURE_MIN_FILTER, &minfilter);
+    glGetTexParameteriv(target, GL_TEXTURE_MAG_FILTER, &magfilter);
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES
-  glGetTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, border_color);
+    glGetTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, border_color);
 #endif
 #endif
+  }
 
 
   GLenum page_target = target;
   GLenum page_target = target;
   if (target == GL_TEXTURE_CUBE_MAP) {
   if (target == GL_TEXTURE_CUBE_MAP) {
@@ -13122,14 +13273,20 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
   tex->set_component_type(type);
   tex->set_component_type(type);
   tex->set_format(format);
   tex->set_format(format);
 
 
-  tex->set_wrap_u(get_panda_wrap_mode(wrap_u));
-  tex->set_wrap_v(get_panda_wrap_mode(wrap_v));
-  tex->set_wrap_w(get_panda_wrap_mode(wrap_w));
-  tex->set_border_color(LColor(border_color[0], border_color[1],
-                               border_color[2], border_color[3]));
+#ifdef OPENGLES
+  if (true) {
+#else
+  if (target != GL_TEXTURE_BUFFER) {
+#endif
+    tex->set_wrap_u(get_panda_wrap_mode(wrap_u));
+    tex->set_wrap_v(get_panda_wrap_mode(wrap_v));
+    tex->set_wrap_w(get_panda_wrap_mode(wrap_w));
+    tex->set_border_color(LColor(border_color[0], border_color[1],
+                                 border_color[2], border_color[3]));
 
 
-  tex->set_minfilter(get_panda_filter_type(minfilter));
-  // tex->set_magfilter(get_panda_filter_type(magfilter));
+    tex->set_minfilter(get_panda_filter_type(minfilter));
+    //tex->set_magfilter(get_panda_filter_type(magfilter));
+  }
 
 
   PTA_uchar image;
   PTA_uchar image;
   size_t page_size = 0;
   size_t page_size = 0;
@@ -13216,6 +13373,13 @@ extract_texture_image(PTA_uchar &image, size_t &page_size,
       }
       }
     }
     }
 
 
+#ifndef OPENGLES
+  } else if (target == GL_TEXTURE_BUFFER) {
+    // In the case of a buffer texture, we need to get it from the buffer.
+    image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_image_size(n));
+    _glGetBufferSubData(target, 0, image.size(), image.p());
+#endif
+
   } else if (compression == Texture::CM_off) {
   } else if (compression == Texture::CM_off) {
     // An uncompressed 1-d, 2-d, or 3-d texture.
     // An uncompressed 1-d, 2-d, or 3-d texture.
     image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_image_size(n));
     image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_image_size(n));

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

@@ -247,6 +247,7 @@ typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VPROC) (GLuint index, const GLuin
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VPROC) (GLuint index, GLenum pname, GLuint64EXT *params);
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VPROC) (GLuint index, GLenum pname, GLuint64EXT *params);
 typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access);
 typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access);
 typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target);
 typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data);
 #endif  // OPENGLES
 #endif  // OPENGLES
 #endif  // __EDG__
 #endif  // __EDG__
 
 
@@ -343,6 +344,12 @@ public:
                        const GeomPrimitivePipelineReader *reader,
                        const GeomPrimitivePipelineReader *reader,
                        bool force);
                        bool force);
 
 
+#ifndef OPENGLES
+  virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data);
+  void apply_shader_buffer(GLuint base, ShaderBuffer *buffer);
+  virtual void release_shader_buffer(BufferContext *bc);
+#endif
+
 #ifndef OPENGLES
 #ifndef OPENGLES
   virtual void begin_occlusion_query();
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
@@ -357,8 +364,6 @@ public:
   virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
   virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
                                           Thread *current_thread);
                                           Thread *current_thread);
 
 
-  virtual PN_stdfloat compute_distance_to(const LPoint3 &point) const;
-
   virtual void clear(DrawableRegion *region);
   virtual void clear(DrawableRegion *region);
 
 
   virtual bool framebuffer_copy_to_texture
   virtual bool framebuffer_copy_to_texture
@@ -686,6 +691,9 @@ protected:
   bool _use_vertex_attrib_binding;
   bool _use_vertex_attrib_binding;
   CPT(GeomVertexFormat) _current_vertex_format;
   CPT(GeomVertexFormat) _current_vertex_format;
   const GeomVertexColumn *_vertex_attrib_columns[32];
   const GeomVertexColumn *_vertex_attrib_columns[32];
+
+  GLuint _current_sbuffer_index;
+  pvector<GLuint> _current_sbuffer_base;
 #endif
 #endif
 
 
   int _active_texture_stage;
   int _active_texture_stage;
@@ -801,6 +809,7 @@ public:
 #ifndef OPENGLES
 #ifndef OPENGLES
   PFNGLMAPBUFFERPROC _glMapBuffer;
   PFNGLMAPBUFFERPROC _glMapBuffer;
   PFNGLUNMAPBUFFERPROC _glUnmapBuffer;
   PFNGLUNMAPBUFFERPROC _glUnmapBuffer;
+  PFNGLGETBUFFERSUBDATAPROC _glGetBufferSubData;
 #endif
 #endif
 
 
 #ifdef OPENGLES
 #ifdef OPENGLES
@@ -811,6 +820,7 @@ public:
 
 
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
   bool _supports_uniform_buffers;
   bool _supports_uniform_buffers;
+  bool _supports_shader_buffers;
   PFNGLBINDBUFFERBASEPROC _glBindBufferBase;
   PFNGLBINDBUFFERBASEPROC _glBindBufferBase;
 
 
   bool _supports_buffer_storage;
   bool _supports_buffer_storage;
@@ -995,6 +1005,9 @@ public:
   PFNGLMAKETEXTUREHANDLENONRESIDENTPROC _glMakeTextureHandleNonResident;
   PFNGLMAKETEXTUREHANDLENONRESIDENTPROC _glMakeTextureHandleNonResident;
   PFNGLUNIFORMHANDLEUI64PROC _glUniformHandleui64;
   PFNGLUNIFORMHANDLEUI64PROC _glUniformHandleui64;
   PFNGLUNIFORMHANDLEUI64VPROC _glUniformHandleui64v;
   PFNGLUNIFORMHANDLEUI64VPROC _glUniformHandleui64v;
+  PFNGLGETPROGRAMINTERFACEIVPROC _glGetProgramInterfaceiv;
+  PFNGLGETPROGRAMRESOURCENAMEPROC _glGetProgramResourceName;
+  PFNGLGETPROGRAMRESOURCEIVPROC _glGetProgramResourceiv;
 #endif  // !OPENGLES
 #endif  // !OPENGLES
 
 
   GLenum _edge_clamp;
   GLenum _edge_clamp;
@@ -1090,6 +1103,7 @@ private:
 
 
   friend class CLP(VertexBufferContext);
   friend class CLP(VertexBufferContext);
   friend class CLP(IndexBufferContext);
   friend class CLP(IndexBufferContext);
+  friend class CLP(BufferContext);
   friend class CLP(ShaderContext);
   friend class CLP(ShaderContext);
   friend class CLP(CgShaderContext);
   friend class CLP(CgShaderContext);
   friend class CLP(GraphicsBuffer);
   friend class CLP(GraphicsBuffer);

+ 59 - 3
panda/src/glstuff/glShaderContext_src.cxx

@@ -324,6 +324,34 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
     }
     }
   }
   }
 
 
+#ifndef OPENGLES
+  // Get the used shader storage blocks.
+  if (_glgsg->_supports_shader_buffers) {
+    GLint block_count = 0, block_maxlength = 0;
+
+    _glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &block_count);
+    _glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &block_maxlength);
+
+    block_maxlength = max(64, block_maxlength);
+    char *block_name_cstr = (char *)alloca(block_maxlength);
+
+    for (int i = 0; i < block_count; ++i) {
+      block_name_cstr[0] = 0;
+      _glgsg->_glGetProgramResourceName(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, block_maxlength, NULL, block_name_cstr);
+
+      const GLenum props[] = {GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE};
+      GLint values[2];
+      _glgsg->_glGetProgramResourceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, NULL, values);
+
+      StorageBlock block;
+      block._name = InternalName::make(block_name_cstr);
+      block._binding_index = values[0];
+      block._min_size = values[1];
+      _storage_blocks.push_back(block);
+    }
+  }
+#endif
+
   // Bind the program, so that we can call glUniform1i for the textures.
   // Bind the program, so that we can call glUniform1i for the textures.
   _glgsg->_glUseProgram(_glsl_program);
   _glgsg->_glUseProgram(_glsl_program);
 
 
@@ -901,7 +929,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       bind._func = Shader::SMF_first;
       bind._func = Shader::SMF_first;
       bind._part[0] = Shader::SMO_attr_material;
       bind._part[0] = Shader::SMO_attr_material;
       bind._arg[0] = NULL;
       bind._arg[0] = NULL;
-      bind._dep[0] = Shader::SSD_general | Shader::SSD_material;
+      bind._dep[0] = Shader::SSD_general | Shader::SSD_material | Shader::SSD_frame;
       bind._part[1] = Shader::SMO_identity;
       bind._part[1] = Shader::SMO_identity;
       bind._arg[1] = NULL;
       bind._arg[1] = NULL;
       bind._dep[1] = Shader::SSD_NONE;
       bind._dep[1] = Shader::SSD_NONE;
@@ -2446,8 +2474,8 @@ update_shader_texture_bindings(ShaderContext *prev) {
         if (gtc != (TextureContext*)NULL) {
         if (gtc != (TextureContext*)NULL) {
           input._gtc = gtc;
           input._gtc = gtc;
 
 
-          gl_tex = gtc->_index;
           _glgsg->update_texture(gtc, true);
           _glgsg->update_texture(gtc, true);
+          gl_tex = gtc->_index;
 
 
           if (gtc->needs_barrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)) {
           if (gtc->needs_barrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)) {
             barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
             barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
@@ -2486,7 +2514,10 @@ update_shader_texture_bindings(ShaderContext *prev) {
           bool has_write = param->has_write_access();
           bool has_write = param->has_write_access();
           input._writable = has_write;
           input._writable = has_write;
 
 
-          if (has_read && has_write) {
+          if (gl_force_image_bindings_writeonly) {
+            access = GL_WRITE_ONLY;
+
+          } else if (has_read && has_write) {
             access = GL_READ_WRITE;
             access = GL_READ_WRITE;
 
 
           } else if (has_read) {
           } else if (has_read) {
@@ -2656,6 +2687,31 @@ update_shader_texture_bindings(ShaderContext *prev) {
   _glgsg->report_my_gl_errors();
   _glgsg->report_my_gl_errors();
 }
 }
 
 
+/**
+ * Updates the shader buffer bindings for this shader.
+ */
+void CLP(ShaderContext)::
+update_shader_buffer_bindings(ShaderContext *prev) {
+#ifndef OPENGLES
+  // Update the shader storage buffer bindings.
+  const ShaderAttrib *attrib = _glgsg->_target_shader;
+
+  for (size_t i = 0; i < _storage_blocks.size(); ++i) {
+    StorageBlock &block = _storage_blocks[i];
+
+    ShaderBuffer *buffer = attrib->get_shader_input_buffer(block._name);
+#ifndef NDEBUG
+    if (buffer->get_data_size_bytes() < block._min_size) {
+      GLCAT.error()
+        << "cannot bind " << *buffer << " to shader because it is too small"
+           " (expected at least " << block._min_size << " bytes)\n";
+    }
+#endif
+    _glgsg->apply_shader_buffer(block._binding_index, buffer);
+  }
+#endif
+}
+
 /**
 /**
  * This subroutine prints the infolog for a shader.
  * This subroutine prints the infolog for a shader.
  */
  */

+ 12 - 0
panda/src/glstuff/glShaderContext_src.h

@@ -55,6 +55,7 @@ public:
   bool update_shader_vertex_arrays(ShaderContext *prev, bool force);
   bool update_shader_vertex_arrays(ShaderContext *prev, bool force);
   void disable_shader_texture_bindings() OVERRIDE;
   void disable_shader_texture_bindings() OVERRIDE;
   void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE;
   void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE;
+  void update_shader_buffer_bindings(ShaderContext *prev) OVERRIDE;
 
 
   INLINE bool uses_standard_vertex_arrays(void);
   INLINE bool uses_standard_vertex_arrays(void);
   INLINE bool uses_custom_vertex_arrays(void);
   INLINE bool uses_custom_vertex_arrays(void);
@@ -87,6 +88,17 @@ private:
   pmap<GLint, GLuint64> _glsl_uniform_handles;
   pmap<GLint, GLuint64> _glsl_uniform_handles;
 #endif
 #endif
 
 
+#ifndef OPENGLES
+  struct StorageBlock {
+    CPT(InternalName) _name;
+    GLuint _binding_index;
+    GLint _min_size;
+  };
+  typedef pvector<StorageBlock> StorageBlocks;
+  StorageBlocks _storage_blocks;
+  BitArray _used_storage_bindings;
+#endif
+
   struct ImageInput {
   struct ImageInput {
     CPT(InternalName) _name;
     CPT(InternalName) _name;
     CLP(TextureContext) *_gtc;
     CLP(TextureContext) *_gtc;

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

@@ -299,6 +299,11 @@ ConfigVariableBool gl_support_shadow_filter
             "cards suffered from a broken implementation of the "
             "cards suffered from a broken implementation of the "
             "shadow map filtering features."));
             "shadow map filtering features."));
 
 
+ConfigVariableBool gl_force_image_bindings_writeonly
+  ("gl-force-image-bindings-writeonly", false,
+   PRC_DESC("Forces all image inputs (not textures!) to be bound as writeonly, "
+            "to read from an image, rebind it as sampler."));
+
 ConfigVariableEnum<CoordinateSystem> gl_coordinate_system
 ConfigVariableEnum<CoordinateSystem> gl_coordinate_system
   ("gl-coordinate-system", CS_yup_right,
   ("gl-coordinate-system", CS_yup_right,
    PRC_DESC("Which coordinate system to use as the internal "
    PRC_DESC("Which coordinate system to use as the internal "
@@ -326,6 +331,7 @@ void CLP(init_classes)() {
   CLP(SamplerContext)::init_type();
   CLP(SamplerContext)::init_type();
 #endif
 #endif
   CLP(VertexBufferContext)::init_type();
   CLP(VertexBufferContext)::init_type();
+  CLP(BufferContext)::init_type();
   CLP(GraphicsBuffer)::init_type();
   CLP(GraphicsBuffer)::init_type();
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES

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

@@ -80,6 +80,7 @@ extern ConfigVariableBool gl_fixed_vertex_attrib_locations;
 extern ConfigVariableBool gl_support_primitive_restart_index;
 extern ConfigVariableBool gl_support_primitive_restart_index;
 extern ConfigVariableBool gl_support_sampler_objects;
 extern ConfigVariableBool gl_support_sampler_objects;
 extern ConfigVariableBool gl_support_shadow_filter;
 extern ConfigVariableBool gl_support_shadow_filter;
+extern ConfigVariableBool gl_force_image_bindings_writeonly;
 extern ConfigVariableEnum<CoordinateSystem> gl_coordinate_system;
 extern ConfigVariableEnum<CoordinateSystem> gl_coordinate_system;
 
 
 extern EXPCL_GL void CLP(init_classes)();
 extern EXPCL_GL void CLP(init_classes)();

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

@@ -21,6 +21,7 @@
 #include "glSamplerContext_src.cxx"
 #include "glSamplerContext_src.cxx"
 #include "glVertexBufferContext_src.cxx"
 #include "glVertexBufferContext_src.cxx"
 #include "glIndexBufferContext_src.cxx"
 #include "glIndexBufferContext_src.cxx"
+#include "glBufferContext_src.cxx"
 #include "glOcclusionQueryContext_src.cxx"
 #include "glOcclusionQueryContext_src.cxx"
 #include "glTimerQueryContext_src.cxx"
 #include "glTimerQueryContext_src.cxx"
 #include "glLatencyQueryContext_src.cxx"
 #include "glLatencyQueryContext_src.cxx"

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

@@ -33,6 +33,7 @@
 #include "glSamplerContext_src.h"
 #include "glSamplerContext_src.h"
 #include "glVertexBufferContext_src.h"
 #include "glVertexBufferContext_src.h"
 #include "glIndexBufferContext_src.h"
 #include "glIndexBufferContext_src.h"
+#include "glBufferContext_src.h"
 #include "glOcclusionQueryContext_src.h"
 #include "glOcclusionQueryContext_src.h"
 #include "glTimerQueryContext_src.h"
 #include "glTimerQueryContext_src.h"
 #include "glLatencyQueryContext_src.h"
 #include "glLatencyQueryContext_src.h"

+ 1 - 0
panda/src/glxdisplay/panda_glxext.h

@@ -199,6 +199,7 @@ GLXContext glXCreateContextAttribsARB (X11_Display *dpy, GLXFBConfig config, GLX
 
 
 #ifndef GLX_ARB_get_proc_address
 #ifndef GLX_ARB_get_proc_address
 #define GLX_ARB_get_proc_address 1
 #define GLX_ARB_get_proc_address 1
+typedef void (*__GLXextFuncPtr)(void);
 typedef __GLXextFuncPtr ( *PFNGLXGETPROCADDRESSARBPROC) (const GLubyte *procName);
 typedef __GLXextFuncPtr ( *PFNGLXGETPROCADDRESSARBPROC) (const GLubyte *procName);
 #ifdef GLX_GLXEXT_PROTOTYPES
 #ifdef GLX_GLXEXT_PROTOTYPES
 __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName);
 __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName);

+ 1 - 0
panda/src/gobj/p3gobj_composite2.cxx

@@ -5,6 +5,7 @@
 #include "samplerContext.cxx"
 #include "samplerContext.cxx"
 #include "samplerState.cxx"
 #include "samplerState.cxx"
 #include "savedContext.cxx"
 #include "savedContext.cxx"
+#include "shaderBuffer.cxx"
 #include "shaderContext.cxx"
 #include "shaderContext.cxx"
 #include "shader.cxx"
 #include "shader.cxx"
 #include "simpleAllocator.cxx"
 #include "simpleAllocator.cxx"

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

@@ -45,6 +45,7 @@ release_all() {
   _texture_residency.set_levels();
   _texture_residency.set_levels();
   _vbuffer_residency.set_levels();
   _vbuffer_residency.set_levels();
   _ibuffer_residency.set_levels();
   _ibuffer_residency.set_levels();
+  _sbuffer_residency.set_levels();
 }
 }
 
 
 /**
 /**
@@ -58,7 +59,8 @@ get_num_queued() const {
           get_num_queued_geoms() +
           get_num_queued_geoms() +
           get_num_queued_shaders() +
           get_num_queued_shaders() +
           get_num_queued_vertex_buffers() +
           get_num_queued_vertex_buffers() +
-          get_num_queued_index_buffers());
+          get_num_queued_index_buffers() +
+          get_num_queued_shader_buffers());
 }
 }
 
 
 /**
 /**
@@ -72,7 +74,8 @@ get_num_prepared() const {
           get_num_prepared_geoms() +
           get_num_prepared_geoms() +
           get_num_prepared_shaders() +
           get_num_prepared_shaders() +
           get_num_prepared_vertex_buffers() +
           get_num_prepared_vertex_buffers() +
-          get_num_prepared_index_buffers());
+          get_num_prepared_index_buffers() +
+          get_num_prepared_shader_buffers());
 }
 }
 
 
 /**
 /**

+ 165 - 0
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -41,6 +41,7 @@ PreparedGraphicsObjects() :
   _texture_residency(_name, "texture"),
   _texture_residency(_name, "texture"),
   _vbuffer_residency(_name, "vbuffer"),
   _vbuffer_residency(_name, "vbuffer"),
   _ibuffer_residency(_name, "ibuffer"),
   _ibuffer_residency(_name, "ibuffer"),
+  _sbuffer_residency(_name, "sbuffer"),
   _graphics_memory_lru("graphics_memory_lru", graphics_memory_limit),
   _graphics_memory_lru("graphics_memory_lru", graphics_memory_limit),
   _sampler_object_lru("sampler_object_lru", sampler_object_limit)
   _sampler_object_lru("sampler_object_lru", sampler_object_limit)
 {
 {
@@ -121,6 +122,16 @@ PreparedGraphicsObjects::
     delete ibc;
     delete ibc;
   }
   }
   _released_index_buffers.clear();
   _released_index_buffers.clear();
+
+  release_all_shader_buffers();
+  Buffers::iterator bci;
+  for (bci = _released_shader_buffers.begin();
+       bci != _released_shader_buffers.end();
+       ++bci) {
+    BufferContext *bc = (BufferContext *)(*bci);
+    delete bc;
+  }
+  _released_shader_buffers.clear();
 }
 }
 
 
 /**
 /**
@@ -167,6 +178,9 @@ show_residency_trackers(ostream &out) const {
 
 
   out << "\nIndex buffers:\n";
   out << "\nIndex buffers:\n";
   _ibuffer_residency.write(out, 2);
   _ibuffer_residency.write(out, 2);
+
+  out << "\nShader buffers:\n";
+  _sbuffer_residency.write(out, 2);
 }
 }
 
 
 /**
 /**
@@ -1195,6 +1209,155 @@ prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg) {
   return ibc;
   return ibc;
 }
 }
 
 
+/**
+ * Indicates that a buffer would like to be put on the list to be prepared
+ * when the GSG is next ready to do this (presumably at the next frame).
+ */
+void PreparedGraphicsObjects::
+enqueue_shader_buffer(ShaderBuffer *data) {
+  ReMutexHolder holder(_lock);
+
+  _enqueued_shader_buffers.insert(data);
+}
+
+/**
+ * Returns true if the index buffer has been queued on this GSG, false
+ * otherwise.
+ */
+bool PreparedGraphicsObjects::
+is_shader_buffer_queued(const ShaderBuffer *data) const {
+  ReMutexHolder holder(_lock);
+
+  EnqueuedShaderBuffers::const_iterator qi = _enqueued_shader_buffers.find((ShaderBuffer *)data);
+  return (qi != _enqueued_shader_buffers.end());
+}
+
+/**
+ * Removes a buffer from the queued list of data arrays to be prepared.
+ * Normally it is not necessary to call this, unless you change your mind
+ * about preparing it at the last minute, since the data will automatically be
+ * dequeued and prepared at the next frame.
+ *
+ * The return value is true if the buffer is successfully dequeued, false if
+ * it had not been queued.
+ */
+bool PreparedGraphicsObjects::
+dequeue_shader_buffer(ShaderBuffer *data) {
+  ReMutexHolder holder(_lock);
+
+  EnqueuedShaderBuffers::iterator qi = _enqueued_shader_buffers.find(data);
+  if (qi != _enqueued_shader_buffers.end()) {
+    _enqueued_shader_buffers.erase(qi);
+    return true;
+  }
+  return false;
+}
+
+/**
+ * Returns true if the index buffer has been prepared on this GSG, false
+ * otherwise.
+ */
+bool PreparedGraphicsObjects::
+is_shader_buffer_prepared(const ShaderBuffer *data) const {
+  return data->is_prepared((PreparedGraphicsObjects *)this);
+}
+
+/**
+ * Indicates that a data context, created by a previous call to
+ * prepare_shader_buffer(), is no longer needed.  The driver resources will not
+ * be freed until some GSG calls update(), indicating it is at a stage where
+ * it is ready to release datas--this prevents conflicts from threading or
+ * multiple GSG's sharing datas (we have no way of knowing which graphics
+ * context is currently active, or what state it's in, at the time
+ * release_shader_buffer is called).
+ */
+void PreparedGraphicsObjects::
+release_shader_buffer(BufferContext *bc) {
+  ReMutexHolder holder(_lock);
+
+  bool removed = (_prepared_shader_buffers.erase(bc) != 0);
+  nassertv(removed);
+
+  _released_shader_buffers.insert(bc);
+}
+
+/**
+ * Releases all datas at once.  This will force them to be reloaded into data
+ * memory for all GSG's that share this object.  Returns the number of datas
+ * released.
+ */
+int PreparedGraphicsObjects::
+release_all_shader_buffers() {
+  ReMutexHolder holder(_lock);
+
+  int num_shader_buffers = (int)_prepared_shader_buffers.size() + (int)_enqueued_shader_buffers.size();
+
+  Buffers::iterator bci;
+  for (bci = _prepared_shader_buffers.begin();
+       bci != _prepared_shader_buffers.end();
+       ++bci) {
+
+    BufferContext *bc = (BufferContext *)(*bci);
+    _released_shader_buffers.insert(bc);
+  }
+
+  _prepared_shader_buffers.clear();
+  _enqueued_shader_buffers.clear();
+
+  return num_shader_buffers;
+}
+
+/**
+ * Returns the number of index buffers that have been enqueued to be prepared
+ * on this GSG.
+ */
+int PreparedGraphicsObjects::
+get_num_queued_shader_buffers() const {
+  return _enqueued_shader_buffers.size();
+}
+
+/**
+ * Returns the number of index buffers that have already been prepared on this
+ * GSG.
+ */
+int PreparedGraphicsObjects::
+get_num_prepared_shader_buffers() const {
+  return _prepared_shader_buffers.size();
+}
+
+/**
+ * Immediately creates a new BufferContext for the indicated data and
+ * returns it.  This assumes that the GraphicsStateGuardian is the currently
+ * active rendering context and that it is ready to accept new datas.  If this
+ * is not necessarily the case, you should use enqueue_shader_buffer() instead.
+ *
+ * Normally, this function is not called directly.  Call Data::prepare_now()
+ * instead.
+ *
+ * The BufferContext contains all of the pertinent information needed by
+ * the GSG to keep track of this one particular data, and will exist as long
+ * as the data is ready to be rendered.
+ *
+ * When either the Data or the PreparedGraphicsObjects object destructs, the
+ * BufferContext will be deleted.
+ */
+BufferContext *PreparedGraphicsObjects::
+prepare_shader_buffer_now(ShaderBuffer *data, GraphicsStateGuardianBase *gsg) {
+  ReMutexHolder holder(_lock);
+
+  // Ask the GSG to create a brand new BufferContext.  There might be
+  // several GSG's sharing the same set of datas; if so, it doesn't matter
+  // which of them creates the context (since they're all shared anyway).
+  BufferContext *bc = gsg->prepare_shader_buffer(data);
+
+  if (bc != (BufferContext *)NULL) {
+    bool prepared = _prepared_shader_buffers.insert(bc).second;
+    nassertr(prepared, bc);
+  }
+
+  return bc;
+}
+
 /**
 /**
  * This is called by the GraphicsStateGuardian to indicate that it is about to
  * This is called by the GraphicsStateGuardian to indicate that it is about to
  * begin processing of the frame.
  * begin processing of the frame.
@@ -1276,6 +1439,7 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
   _texture_residency.begin_frame(current_thread);
   _texture_residency.begin_frame(current_thread);
   _vbuffer_residency.begin_frame(current_thread);
   _vbuffer_residency.begin_frame(current_thread);
   _ibuffer_residency.begin_frame(current_thread);
   _ibuffer_residency.begin_frame(current_thread);
+  _sbuffer_residency.begin_frame(current_thread);
 
 
   // Now prepare all the textures, geoms, and buffers awaiting preparation.
   // Now prepare all the textures, geoms, and buffers awaiting preparation.
   EnqueuedTextures::iterator qti;
   EnqueuedTextures::iterator qti;
@@ -1359,6 +1523,7 @@ end_frame(Thread *current_thread) {
   _texture_residency.end_frame(current_thread);
   _texture_residency.end_frame(current_thread);
   _vbuffer_residency.end_frame(current_thread);
   _vbuffer_residency.end_frame(current_thread);
   _ibuffer_residency.end_frame(current_thread);
   _ibuffer_residency.end_frame(current_thread);
+  _sbuffer_residency.end_frame(current_thread);
 }
 }
 
 
 /**
 /**

+ 19 - 0
panda/src/gobj/preparedGraphicsObjects.h

@@ -22,6 +22,7 @@
 #include "geomVertexArrayData.h"
 #include "geomVertexArrayData.h"
 #include "geomPrimitive.h"
 #include "geomPrimitive.h"
 #include "shader.h"
 #include "shader.h"
+#include "shaderBuffer.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
 #include "pset.h"
 #include "pset.h"
@@ -35,6 +36,7 @@ class GeomContext;
 class ShaderContext;
 class ShaderContext;
 class VertexBufferContext;
 class VertexBufferContext;
 class IndexBufferContext;
 class IndexBufferContext;
+class BufferContext;
 class GraphicsStateGuardianBase;
 class GraphicsStateGuardianBase;
 
 
 /**
 /**
@@ -142,6 +144,19 @@ PUBLISHED:
   prepare_index_buffer_now(GeomPrimitive *data,
   prepare_index_buffer_now(GeomPrimitive *data,
                            GraphicsStateGuardianBase *gsg);
                            GraphicsStateGuardianBase *gsg);
 
 
+  void enqueue_shader_buffer(ShaderBuffer *data);
+  bool is_shader_buffer_queued(const ShaderBuffer *data) const;
+  bool dequeue_shader_buffer(ShaderBuffer *data);
+  bool is_shader_buffer_prepared(const ShaderBuffer *data) const;
+  void release_shader_buffer(BufferContext *bc);
+  int release_all_shader_buffers();
+  int get_num_queued_shader_buffers() const;
+  int get_num_prepared_shader_buffers() const;
+
+  BufferContext *
+  prepare_shader_buffer_now(ShaderBuffer *data,
+                            GraphicsStateGuardianBase *gsg);
+
 public:
 public:
   void begin_frame(GraphicsStateGuardianBase *gsg,
   void begin_frame(GraphicsStateGuardianBase *gsg,
                    Thread *current_thread);
                    Thread *current_thread);
@@ -160,6 +175,7 @@ private:
   typedef phash_set<BufferContext *, pointer_hash> Buffers;
   typedef phash_set<BufferContext *, pointer_hash> Buffers;
   typedef phash_set< PT(GeomVertexArrayData) > EnqueuedVertexBuffers;
   typedef phash_set< PT(GeomVertexArrayData) > EnqueuedVertexBuffers;
   typedef phash_set< PT(GeomPrimitive) > EnqueuedIndexBuffers;
   typedef phash_set< PT(GeomPrimitive) > EnqueuedIndexBuffers;
+  typedef phash_set< PT(ShaderBuffer) > EnqueuedShaderBuffers;
 
 
   // Sampler states are stored a little bit differently, as they are mapped by
   // Sampler states are stored a little bit differently, as they are mapped by
   // value and can't store the list of prepared samplers.
   // value and can't store the list of prepared samplers.
@@ -207,6 +223,8 @@ private:
   EnqueuedVertexBuffers _enqueued_vertex_buffers;
   EnqueuedVertexBuffers _enqueued_vertex_buffers;
   Buffers _prepared_index_buffers, _released_index_buffers;
   Buffers _prepared_index_buffers, _released_index_buffers;
   EnqueuedIndexBuffers _enqueued_index_buffers;
   EnqueuedIndexBuffers _enqueued_index_buffers;
+  Buffers _prepared_shader_buffers, _released_shader_buffers;
+  EnqueuedShaderBuffers _enqueued_shader_buffers;
 
 
   BufferCache _vertex_buffer_cache;
   BufferCache _vertex_buffer_cache;
   BufferCacheLRU _vertex_buffer_cache_lru;
   BufferCacheLRU _vertex_buffer_cache_lru;
@@ -220,6 +238,7 @@ public:
   BufferResidencyTracker _texture_residency;
   BufferResidencyTracker _texture_residency;
   BufferResidencyTracker _vbuffer_residency;
   BufferResidencyTracker _vbuffer_residency;
   BufferResidencyTracker _ibuffer_residency;
   BufferResidencyTracker _ibuffer_residency;
+  BufferResidencyTracker _sbuffer_residency;
 
 
   AdaptiveLru _graphics_memory_lru;
   AdaptiveLru _graphics_memory_lru;
   SimpleLru _sampler_object_lru;
   SimpleLru _sampler_object_lru;

+ 37 - 4
panda/src/gobj/shader.cxx

@@ -376,7 +376,7 @@ cp_dependency(ShaderMatInput inp) {
     return SSD_NONE;
     return SSD_NONE;
   }
   }
   if (inp == SMO_attr_material || inp == SMO_attr_material2) {
   if (inp == SMO_attr_material || inp == SMO_attr_material2) {
-    dep |= SSD_material;
+    dep |= SSD_material | SSD_frame;
   }
   }
   if (inp == SMO_attr_color) {
   if (inp == SMO_attr_color) {
     dep |= SSD_color;
     dep |= SSD_color;
@@ -2380,6 +2380,12 @@ do_read_source(string &into, const Filename &fn, BamCacheRecord *record) {
     _last_modified = max(_last_modified, vf->get_timestamp());
     _last_modified = max(_last_modified, vf->get_timestamp());
     _source_files.push_back(vf->get_filename());
     _source_files.push_back(vf->get_filename());
   }
   }
+
+  // Strip trailing whitespace.
+  while (!into.empty() && isspace(into[into.size() - 1])) {
+    into.resize(into.size() - 1);
+  }
+
   return true;
   return true;
 }
 }
 
 
@@ -2521,6 +2527,11 @@ r_preprocess_source(ostream &out, const Filename &fn,
       line += line2.substr(block_end + 2);
       line += line2.substr(block_end + 2);
     }
     }
 
 
+    // Strip trailing whitespace.
+    while (!line.empty() && isspace(line[line.size() - 1])) {
+      line.resize(line.size() - 1);
+    }
+
     // Check if this line contains a #directive.
     // Check if this line contains a #directive.
     char directive[64];
     char directive[64];
     if (line.size() < 8 || sscanf(line.c_str(), " # %63s", directive) != 1) {
     if (line.size() < 8 || sscanf(line.c_str(), " # %63s", directive) != 1) {
@@ -2940,6 +2951,14 @@ load(const Filename &file, ShaderLanguage lang) {
   }
   }
 
 
   _load_table[sfile] = shader;
   _load_table[sfile] = shader;
+
+  if (cache_generated_shaders) {
+    ShaderTable::const_iterator i = _make_table.find(shader->_text);
+    if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
+      return i->second;
+    }
+    _make_table[shader->_text] = shader;
+  }
   return shader;
   return shader;
 }
 }
 
 
@@ -2970,6 +2989,14 @@ load(ShaderLanguage lang, const Filename &vertex,
   }
   }
 
 
   _load_table[sfile] = shader;
   _load_table[sfile] = shader;
+
+  if (cache_generated_shaders) {
+    ShaderTable::const_iterator i = _make_table.find(shader->_text);
+    if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
+      return i->second;
+    }
+    _make_table[shader->_text] = shader;
+  }
   return shader;
   return shader;
 }
 }
 
 
@@ -3025,14 +3052,21 @@ load_compute(ShaderLanguage lang, const Filename &fn) {
   if (!shader->read(sfile, record)) {
   if (!shader->read(sfile, record)) {
     return NULL;
     return NULL;
   }
   }
+  _load_table[sfile] = shader;
+
+  if (cache_generated_shaders) {
+    ShaderTable::const_iterator i = _make_table.find(shader->_text);
+    if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
+      return i->second;
+    }
+    _make_table[shader->_text] = shader;
+  }
 
 
   // It makes little sense to cache the shader before compilation, so we keep
   // It makes little sense to cache the shader before compilation, so we keep
   // the record for when we have the compiled the shader.
   // the record for when we have the compiled the shader.
   swap(shader->_record, record);
   swap(shader->_record, record);
   shader->_cache_compiled_shader = BamCache::get_global_ptr()->get_cache_compiled_shaders();
   shader->_cache_compiled_shader = BamCache::get_global_ptr()->get_cache_compiled_shaders();
   shader->_fullpath = shader->_source_files[0];
   shader->_fullpath = shader->_source_files[0];
-
-  _load_table[sfile] = shader;
   return shader;
   return shader;
 }
 }
 
 
@@ -3166,7 +3200,6 @@ make_compute(ShaderLanguage lang, const string &body) {
   sbody._separate = true;
   sbody._separate = true;
   sbody._compute = body;
   sbody._compute = body;
 
 
-
   if (cache_generated_shaders) {
   if (cache_generated_shaders) {
     ShaderTable::const_iterator i = _make_table.find(sbody);
     ShaderTable::const_iterator i = _make_table.find(sbody);
     if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
     if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {

+ 63 - 0
panda/src/gobj/shaderBuffer.I

@@ -0,0 +1,63 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderBuffer.I
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+/**
+ * Creates an uninitialized buffer object with the given size.  For now, these
+ * parameters cannot be modified, but this may change in the future.
+ */
+INLINE ShaderBuffer::
+ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint) :
+  Namable(name),
+  _data_size_bytes(size),
+  _usage_hint(usage_hint) {
+}
+
+/**
+ * Creates a buffer object initialized with the given data.  For now, these
+ * parameters cannot be modified, but this may change in the future.
+ */
+INLINE ShaderBuffer::
+ShaderBuffer(const string &name, pvector<unsigned char> initial_data, UsageHint usage_hint) :
+  Namable(name),
+  _data_size_bytes(initial_data.size()),
+  _usage_hint(usage_hint),
+  _initial_data(initial_data) {
+}
+
+/**
+ * Returns the buffer size in bytes.
+ */
+INLINE uint64_t ShaderBuffer::
+get_data_size_bytes() const {
+  return _data_size_bytes;
+}
+
+/**
+ * Returns the buffer usage hint.
+ */
+INLINE GeomEnums::UsageHint ShaderBuffer::
+get_usage_hint() const {
+  return _usage_hint;
+}
+
+/**
+ * Returns a pointer to the initial buffer data, or NULL if not specified.
+ */
+INLINE const unsigned char *ShaderBuffer::
+get_initial_data() const {
+  if (_initial_data.empty()) {
+    return NULL;
+  } else {
+    return &_initial_data[0];
+  }
+}

+ 193 - 0
panda/src/gobj/shaderBuffer.cxx

@@ -0,0 +1,193 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderBuffer.cxx
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+#include "shaderBuffer.h"
+#include "preparedGraphicsObjects.h"
+
+TypeHandle ShaderBuffer::_type_handle;
+
+/**
+ *
+ */
+void ShaderBuffer::
+output(ostream &out) const {
+  out << "buffer " << get_name() << ", " << _data_size_bytes << "B, " << _usage_hint;
+}
+
+/**
+ * Indicates that the data should be enqueued to be prepared in the indicated
+ * prepared_objects at the beginning of the next frame.  This will ensure the
+ * data is already loaded into the GSG if it is expected to be rendered soon.
+ *
+ * Use this function instead of prepare_now() to preload datas from a user
+ * interface standpoint.
+ */
+void ShaderBuffer::
+prepare(PreparedGraphicsObjects *prepared_objects) {
+  prepared_objects->enqueue_shader_buffer(this);
+}
+
+/**
+ * Returns true if the data has already been prepared or enqueued for
+ * preparation on the indicated GSG, false otherwise.
+ */
+bool ShaderBuffer::
+is_prepared(PreparedGraphicsObjects *prepared_objects) const {
+  if (_contexts == (Contexts *)NULL) {
+    return false;
+  }
+  Contexts::const_iterator ci;
+  ci = _contexts->find(prepared_objects);
+  if (ci != _contexts->end()) {
+    return true;
+  }
+  return prepared_objects->is_shader_buffer_queued(this);
+}
+
+/**
+ * Creates a context for the data on the particular GSG, if it does not
+ * already exist.  Returns the new (or old) BufferContext.  This assumes
+ * that the GraphicsStateGuardian is the currently active rendering context
+ * and that it is ready to accept new datas.  If this is not necessarily the
+ * case, you should use prepare() instead.
+ *
+ * Normally, this is not called directly except by the GraphicsStateGuardian;
+ * a data does not need to be explicitly prepared by the user before it may be
+ * rendered.
+ */
+BufferContext *ShaderBuffer::
+prepare_now(PreparedGraphicsObjects *prepared_objects,
+            GraphicsStateGuardianBase *gsg) {
+  if (_contexts == (Contexts *)NULL) {
+    _contexts = new Contexts;
+  }
+  Contexts::const_iterator ci;
+  ci = _contexts->find(prepared_objects);
+  if (ci != _contexts->end()) {
+    return (*ci).second;
+  }
+
+  BufferContext *vbc = prepared_objects->prepare_shader_buffer_now(this, gsg);
+  if (vbc != (BufferContext *)NULL) {
+    (*_contexts)[prepared_objects] = vbc;
+  }
+  return vbc;
+}
+
+/**
+ * Frees the data context only on the indicated object, if it exists there.
+ * Returns true if it was released, false if it had not been prepared.
+ */
+bool ShaderBuffer::
+release(PreparedGraphicsObjects *prepared_objects) {
+  if (_contexts != (Contexts *)NULL) {
+    Contexts::iterator ci;
+    ci = _contexts->find(prepared_objects);
+    if (ci != _contexts->end()) {
+      BufferContext *vbc = (*ci).second;
+      prepared_objects->release_shader_buffer(vbc);
+      return true;
+    }
+  }
+
+  // Maybe it wasn't prepared yet, but it's about to be.
+  return prepared_objects->dequeue_shader_buffer(this);
+}
+
+/**
+ * Frees the context allocated on all objects for which the data has been
+ * declared.  Returns the number of contexts which have been freed.
+ */
+int ShaderBuffer::
+release_all() {
+  int num_freed = 0;
+
+  if (_contexts != (Contexts *)NULL) {
+    // We have to traverse a copy of the _contexts list, because the
+    // PreparedGraphicsObjects object will call clear_prepared() in response
+    // to each release_shader_buffer(), and we don't want to be modifying the
+    // _contexts list while we're traversing it.
+    Contexts temp = *_contexts;
+    num_freed = (int)_contexts->size();
+
+    Contexts::const_iterator ci;
+    for (ci = temp.begin(); ci != temp.end(); ++ci) {
+      PreparedGraphicsObjects *prepared_objects = (*ci).first;
+      BufferContext *vbc = (*ci).second;
+      prepared_objects->release_shader_buffer(vbc);
+    }
+
+    // Now that we've called release_shader_buffer() on every known context,
+    // the _contexts list should have completely emptied itself.
+    nassertr(_contexts == NULL, num_freed);
+  }
+
+  return num_freed;
+}
+
+/**
+ * Tells the BamReader how to create objects of type ParamValue.
+ */
+void ShaderBuffer::
+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 ShaderBuffer::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  dg.add_string(get_name());
+  dg.add_uint64(_data_size_bytes);
+  dg.add_uint8(_usage_hint);
+  dg.add_bool(!_initial_data.empty());
+  dg.append_data(_initial_data.data(), _initial_data.size());
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type ParamValue is encountered in the Bam file.  It should create the
+ * ParamValue and extract its information from the file.
+ */
+TypedWritable *ShaderBuffer::
+make_from_bam(const FactoryParams &params) {
+  ShaderBuffer *param = new ShaderBuffer;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new ParamValue.
+ */
+void ShaderBuffer::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  set_name(scan.get_string());
+  _data_size_bytes = scan.get_uint64();
+  _usage_hint = (UsageHint)scan.get_uint8();
+
+  if (scan.get_bool() && _data_size_bytes > 0) {
+    nassertv_always(_data_size_bytes <= scan.get_remaining_size());
+    _initial_data.resize(_data_size_bytes);
+    scan.extract_bytes(&_initial_data[0], _data_size_bytes);
+  } else {
+    _initial_data.clear();
+  }
+}

+ 100 - 0
panda/src/gobj/shaderBuffer.h

@@ -0,0 +1,100 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderBuffer.h
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+#ifndef SHADERBUFFER_H
+#define SHADERBUFFER_H
+
+#include "pandabase.h"
+#include "typedWritableReferenceCount.h"
+#include "namable.h"
+#include "geomEnums.h"
+#include "graphicsStateGuardianBase.h"
+#include "factoryParams.h"
+
+class BufferContext;
+class PreparedGraphicsObjects;
+
+/**
+ * This is a generic buffer object that lives in graphics memory.
+ */
+class EXPCL_PANDA_GOBJ ShaderBuffer : public TypedWritableReferenceCount, public Namable, public GeomEnums {
+private:
+  INLINE ShaderBuffer() DEFAULT_CTOR;
+
+PUBLISHED:
+  INLINE ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint);
+  INLINE ShaderBuffer(const string &name, pvector<unsigned char> initial_data, UsageHint usage_hint);
+
+public:
+  INLINE uint64_t get_data_size_bytes() const;
+  INLINE UsageHint get_usage_hint() const;
+  INLINE const unsigned char *get_initial_data() const;
+
+  virtual void output(ostream &out) const;
+
+PUBLISHED:
+  MAKE_PROPERTY(data_size_bytes, get_data_size_bytes);
+  MAKE_PROPERTY(usage_hint, get_usage_hint);
+
+  void prepare(PreparedGraphicsObjects *prepared_objects);
+  bool is_prepared(PreparedGraphicsObjects *prepared_objects) const;
+
+  BufferContext *prepare_now(PreparedGraphicsObjects *prepared_objects,
+                             GraphicsStateGuardianBase *gsg);
+  bool release(PreparedGraphicsObjects *prepared_objects);
+  int release_all();
+
+private:
+  uint64_t _data_size_bytes;
+  UsageHint _usage_hint;
+  pvector<unsigned char> _initial_data;
+
+  typedef pmap<PreparedGraphicsObjects *, BufferContext *> Contexts;
+  Contexts *_contexts;
+
+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 {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    Namable::init_type();
+    register_type(_type_handle, "ShaderBuffer",
+                  TypedWritableReferenceCount::get_class_type(),
+                  Namable::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+INLINE ostream &operator << (ostream &out, const ShaderBuffer &m) {
+  m.output(out);
+  return out;
+}
+
+#include "shaderBuffer.I"
+
+#endif

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

@@ -42,6 +42,7 @@ public:
   INLINE virtual bool update_shader_vertex_arrays(ShaderContext *prev, bool force) { return false; };
   INLINE virtual bool update_shader_vertex_arrays(ShaderContext *prev, bool force) { return false; };
   INLINE virtual void disable_shader_texture_bindings() {};
   INLINE virtual void disable_shader_texture_bindings() {};
   INLINE virtual void update_shader_texture_bindings(ShaderContext *prev) {};
   INLINE virtual void update_shader_texture_bindings(ShaderContext *prev) {};
+  INLINE virtual void update_shader_buffer_bindings(ShaderContext *prev) {};
 
 
   INLINE virtual bool uses_standard_vertex_arrays(void) { return true; };
   INLINE virtual bool uses_standard_vertex_arrays(void) { return true; };
   INLINE virtual bool uses_custom_vertex_arrays(void) { return false; };
   INLINE virtual bool uses_custom_vertex_arrays(void) { return false; };

+ 146 - 24
panda/src/gobj/texture.cxx

@@ -3643,13 +3643,13 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only)
       header.pf.four_cc == 0x30315844) {   // 'DX10'
       header.pf.four_cc == 0x30315844) {   // 'DX10'
     // A DirectX 10 style texture, which has an additional header.
     // A DirectX 10 style texture, which has an additional header.
     func = read_dds_level_generic_uncompressed;
     func = read_dds_level_generic_uncompressed;
-    unsigned int format = dds.get_uint32();
+    unsigned int dxgi_format = dds.get_uint32();
     unsigned int dimension = dds.get_uint32();
     unsigned int dimension = dds.get_uint32();
     unsigned int misc_flag = dds.get_uint32();
     unsigned int misc_flag = dds.get_uint32();
     unsigned int array_size = dds.get_uint32();
     unsigned int array_size = dds.get_uint32();
     /*unsigned int alpha_mode = */dds.get_uint32();
     /*unsigned int alpha_mode = */dds.get_uint32();
 
 
-    switch (format) {
+    switch (dxgi_format) {
     case 2:    // DXGI_FORMAT_R32G32B32A32_FLOAT
     case 2:    // DXGI_FORMAT_R32G32B32A32_FLOAT
       format = F_rgba32;
       format = F_rgba32;
       component_type = T_float;
       component_type = T_float;
@@ -3665,6 +3665,11 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only)
       component_type = T_unsigned_short;
       component_type = T_unsigned_short;
       func = read_dds_level_abgr16;
       func = read_dds_level_abgr16;
       break;
       break;
+    case 16:   // DXGI_FORMAT_R32G32_FLOAT
+      format = F_rg32;
+      component_type = T_float;
+      func = read_dds_level_raw;
+      break;
     case 27:   // DXGI_FORMAT_R8G8B8A8_TYPELESS
     case 27:   // DXGI_FORMAT_R8G8B8A8_TYPELESS
     case 28:   // DXGI_FORMAT_R8G8B8A8_UNORM
     case 28:   // DXGI_FORMAT_R8G8B8A8_UNORM
       format = F_rgba8;
       format = F_rgba8;
@@ -3688,6 +3693,41 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only)
       component_type = T_byte;
       component_type = T_byte;
       func = read_dds_level_abgr8;
       func = read_dds_level_abgr8;
       break;
       break;
+    case 34:   // DXGI_FORMAT_R16G16_FLOAT:
+      format = F_rg16;
+      component_type = T_half_float;
+      func = read_dds_level_raw;
+      break;
+    case 35:   // DXGI_FORMAT_R16G16_UNORM:
+      format = F_rg16;
+      component_type = T_unsigned_short;
+      func = read_dds_level_raw;
+      break;
+    case 37:   // DXGI_FORMAT_R16G16_SNORM:
+      format = F_rg16;
+      component_type = T_short;
+      func = read_dds_level_raw;
+      break;
+    case 40:   // DXGI_FORMAT_D32_FLOAT
+      format = F_depth_component32;
+      component_type = T_float;
+      func = read_dds_level_raw;
+      break;
+    case 41:   // DXGI_FORMAT_R32_FLOAT
+      format = F_r32;
+      component_type = T_float;
+      func = read_dds_level_raw;
+      break;
+    case 42:   // DXGI_FORMAT_R32_UINT
+      format = F_r32i;
+      component_type = T_unsigned_int;
+      func = read_dds_level_raw;
+      break;
+    case 43:   // DXGI_FORMAT_R32_SINT
+      format = F_r32i;
+      component_type = T_int;
+      func = read_dds_level_raw;
+      break;
     case 48:   // DXGI_FORMAT_R8G8_TYPELESS
     case 48:   // DXGI_FORMAT_R8G8_TYPELESS
     case 49:   // DXGI_FORMAT_R8G8_UNORM
     case 49:   // DXGI_FORMAT_R8G8_UNORM
       format = F_rg;
       format = F_rg;
@@ -3703,6 +3743,36 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only)
       format = F_rg8i;
       format = F_rg8i;
       component_type = T_byte;
       component_type = T_byte;
       break;
       break;
+    case 54:   // DXGI_FORMAT_R16_FLOAT:
+      format = F_r16;
+      component_type = T_half_float;
+      func = read_dds_level_raw;
+      break;
+    case 55:   // DXGI_FORMAT_D16_UNORM:
+      format = F_depth_component16;
+      component_type = T_unsigned_short;
+      func = read_dds_level_raw;
+      break;
+    case 56:   // DXGI_FORMAT_R16_UNORM:
+      format = F_r16;
+      component_type = T_unsigned_short;
+      func = read_dds_level_raw;
+      break;
+    case 57:   // DXGI_FORMAT_R16_UINT:
+      format = F_r16i;
+      component_type = T_unsigned_short;
+      func = read_dds_level_raw;
+      break;
+    case 58:   // DXGI_FORMAT_R16_SNORM:
+      format = F_r16;
+      component_type = T_short;
+      func = read_dds_level_raw;
+      break;
+    case 59:   // DXGI_FORMAT_R16_SINT:
+      format = F_r16i;
+      component_type = T_short;
+      func = read_dds_level_raw;
+      break;
     case 60:   // DXGI_FORMAT_R8_TYPELESS
     case 60:   // DXGI_FORMAT_R8_TYPELESS
     case 61:   // DXGI_FORMAT_R8_UNORM
     case 61:   // DXGI_FORMAT_R8_UNORM
       format = F_red;
       format = F_red;
@@ -3760,7 +3830,6 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only)
       compression = CM_rgtc;
       compression = CM_rgtc;
       func = read_dds_level_bc4;
       func = read_dds_level_bc4;
       break;
       break;
-      break;
     case 82:   // DXGI_FORMAT_BC5_TYPELESS
     case 82:   // DXGI_FORMAT_BC5_TYPELESS
     case 83:   // DXGI_FORMAT_BC5_UNORM
     case 83:   // DXGI_FORMAT_BC5_UNORM
       format = F_rg;
       format = F_rg;
@@ -3786,7 +3855,7 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only)
       break;
       break;
     default:
     default:
       gobj_cat.error()
       gobj_cat.error()
-        << filename << ": unsupported DXGI format " << format << ".\n";
+        << filename << ": unsupported DXGI format " << dxgi_format << ".\n";
       return false;
       return false;
     }
     }
 
 
@@ -7411,6 +7480,9 @@ 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 {
+  if (cdata->_texture_type == Texture::TT_buffer_texture) {
+    return 1;
+  }
   int size = max(cdata->_x_size, cdata->_y_size);
   int size = max(cdata->_x_size, cdata->_y_size);
   if (cdata->_texture_type == Texture::TT_3d_texture) {
   if (cdata->_texture_type == Texture::TT_3d_texture) {
     size = max(size, cdata->_z_size);
     size = max(size, cdata->_z_size);
@@ -7726,10 +7798,11 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size,
     // Most common case: one byte per pixel, and the source image shows a
     // Most common case: one byte per pixel, and the source image shows a
     // maxval of 255.  No scaling is necessary.  Because this is such a common
     // maxval of 255.  No scaling is necessary.  Because this is such a common
     // case, we break it out per component for best performance.
     // case, we break it out per component for best performance.
+    const xel *array = pnmimage.get_array();
     switch (num_components) {
     switch (num_components) {
     case 1:
     case 1:
       for (int j = y_size-1; j >= 0; j--) {
       for (int j = y_size-1; j >= 0; j--) {
-        xel *row = pnmimage.row(j);
+        const xel *row = array + j * x_size;
         for (int i = 0; i < x_size; i++) {
         for (int i = 0; i < x_size; i++) {
           *p++ = (uchar)PPM_GETB(row[i]);
           *p++ = (uchar)PPM_GETB(row[i]);
         }
         }
@@ -7739,9 +7812,10 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size,
 
 
     case 2:
     case 2:
       if (img_has_alpha) {
       if (img_has_alpha) {
+        const xelval *alpha = pnmimage.get_alpha_array();
         for (int j = y_size-1; j >= 0; j--) {
         for (int j = y_size-1; j >= 0; j--) {
-          xel *row = pnmimage.row(j);
-          xelval *alpha_row = pnmimage.alpha_row(j);
+          const xel *row = array + j * x_size;
+          const xelval *alpha_row = alpha + j * x_size;
           for (int i = 0; i < x_size; i++) {
           for (int i = 0; i < x_size; i++) {
             *p++ = (uchar)PPM_GETB(row[i]);
             *p++ = (uchar)PPM_GETB(row[i]);
             *p++ = (uchar)alpha_row[i];
             *p++ = (uchar)alpha_row[i];
@@ -7750,7 +7824,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size,
         }
         }
       } else {
       } else {
         for (int j = y_size-1; j >= 0; j--) {
         for (int j = y_size-1; j >= 0; j--) {
-          xel *row = pnmimage.row(j);
+          const xel *row = array + j * x_size;
           for (int i = 0; i < x_size; i++) {
           for (int i = 0; i < x_size; i++) {
             *p++ = (uchar)PPM_GETB(row[i]);
             *p++ = (uchar)PPM_GETB(row[i]);
             *p++ = (uchar)255;
             *p++ = (uchar)255;
@@ -7762,7 +7836,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size,
 
 
     case 3:
     case 3:
       for (int j = y_size-1; j >= 0; j--) {
       for (int j = y_size-1; j >= 0; j--) {
-        xel *row = pnmimage.row(j);
+        const xel *row = array + j * x_size;
         for (int i = 0; i < x_size; i++) {
         for (int i = 0; i < x_size; i++) {
           *p++ = (uchar)PPM_GETB(row[i]);
           *p++ = (uchar)PPM_GETB(row[i]);
           *p++ = (uchar)PPM_GETG(row[i]);
           *p++ = (uchar)PPM_GETG(row[i]);
@@ -7774,9 +7848,10 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size,
 
 
     case 4:
     case 4:
       if (img_has_alpha) {
       if (img_has_alpha) {
+        const xelval *alpha = pnmimage.get_alpha_array();
         for (int j = y_size-1; j >= 0; j--) {
         for (int j = y_size-1; j >= 0; j--) {
-          xel *row = pnmimage.row(j);
-          xelval *alpha_row = pnmimage.alpha_row(j);
+          const xel *row = array + j * x_size;
+          const xelval *alpha_row = alpha + j * x_size;
           for (int i = 0; i < x_size; i++) {
           for (int i = 0; i < x_size; i++) {
             *p++ = (uchar)PPM_GETB(row[i]);
             *p++ = (uchar)PPM_GETB(row[i]);
             *p++ = (uchar)PPM_GETG(row[i]);
             *p++ = (uchar)PPM_GETG(row[i]);
@@ -7787,7 +7862,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size,
         }
         }
       } else {
       } else {
         for (int j = y_size-1; j >= 0; j--) {
         for (int j = y_size-1; j >= 0; j--) {
-          xel *row = pnmimage.row(j);
+          const xel *row = array + j * x_size;
           for (int i = 0; i < x_size; i++) {
           for (int i = 0; i < x_size; i++) {
             *p++ = (uchar)PPM_GETB(row[i]);
             *p++ = (uchar)PPM_GETB(row[i]);
             *p++ = (uchar)PPM_GETG(row[i]);
             *p++ = (uchar)PPM_GETG(row[i]);
@@ -7810,7 +7885,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size,
     for (int j = y_size-1; j >= 0; j--) {
     for (int j = y_size-1; j >= 0; j--) {
       for (int i = 0; i < x_size; i++) {
       for (int i = 0; i < x_size; i++) {
         if (is_grayscale) {
         if (is_grayscale) {
-           store_unscaled_short(p, pnmimage.get_gray_val(i, j));
+          store_unscaled_short(p, pnmimage.get_gray_val(i, j));
         } else {
         } else {
           store_unscaled_short(p, pnmimage.get_blue_val(i, j));
           store_unscaled_short(p, pnmimage.get_blue_val(i, j));
           store_unscaled_short(p, pnmimage.get_green_val(i, j));
           store_unscaled_short(p, pnmimage.get_green_val(i, j));
@@ -7976,11 +8051,13 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
   const unsigned char *p = &image[idx];
   const unsigned char *p = &image[idx];
 
 
   if (component_width == 1) {
   if (component_width == 1) {
+    xel *array = pnmimage.get_array();
     if (is_grayscale) {
     if (is_grayscale) {
       if (has_alpha) {
       if (has_alpha) {
+        xelval *alpha = pnmimage.get_alpha_array();
         for (int j = y_size-1; j >= 0; j--) {
         for (int j = y_size-1; j >= 0; j--) {
-          xel *row = pnmimage.row(j);
-          xelval *alpha_row = pnmimage.alpha_row(j);
+          xel *row = array + j * x_size;
+          xelval *alpha_row = alpha + j * x_size;
           for (int i = 0; i < x_size; i++) {
           for (int i = 0; i < x_size; i++) {
             PPM_PUTB(row[i], *p++);
             PPM_PUTB(row[i], *p++);
             alpha_row[i] = *p++;
             alpha_row[i] = *p++;
@@ -7988,7 +8065,7 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
         }
         }
       } else {
       } else {
         for (int j = y_size-1; j >= 0; j--) {
         for (int j = y_size-1; j >= 0; j--) {
-          xel *row = pnmimage.row(j);
+          xel *row = array + j * x_size;
           for (int i = 0; i < x_size; i++) {
           for (int i = 0; i < x_size; i++) {
             PPM_PUTB(row[i], *p++);
             PPM_PUTB(row[i], *p++);
           }
           }
@@ -7996,9 +8073,10 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
       }
       }
     } else {
     } else {
       if (has_alpha) {
       if (has_alpha) {
+        xelval *alpha = pnmimage.get_alpha_array();
         for (int j = y_size-1; j >= 0; j--) {
         for (int j = y_size-1; j >= 0; j--) {
-          xel *row = pnmimage.row(j);
-          xelval *alpha_row = pnmimage.alpha_row(j);
+          xel *row = array + j * x_size;
+          xelval *alpha_row = alpha + j * x_size;
           for (int i = 0; i < x_size; i++) {
           for (int i = 0; i < x_size; i++) {
             PPM_PUTB(row[i], *p++);
             PPM_PUTB(row[i], *p++);
             PPM_PUTG(row[i], *p++);
             PPM_PUTG(row[i], *p++);
@@ -8008,7 +8086,7 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
         }
         }
       } else {
       } else {
         for (int j = y_size-1; j >= 0; j--) {
         for (int j = y_size-1; j >= 0; j--) {
-          xel *row = pnmimage.row(j);
+          xel *row = array + j * x_size;
           for (int i = 0; i < x_size; i++) {
           for (int i = 0; i < x_size; i++) {
             PPM_PUTB(row[i], *p++);
             PPM_PUTB(row[i], *p++);
             PPM_PUTG(row[i], *p++);
             PPM_PUTG(row[i], *p++);
@@ -8255,6 +8333,7 @@ read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header, int n
 
 
   size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
   size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
   size_t row_bytes = x_size * 16;
   size_t row_bytes = x_size * 16;
+  nassertr(row_bytes * y_size == size, PTA_uchar());
   PTA_uchar image = PTA_uchar::empty_array(size);
   PTA_uchar image = PTA_uchar::empty_array(size);
   for (int y = y_size - 1; y >= 0; --y) {
   for (int y = y_size - 1; y >= 0; --y) {
     unsigned char *p = image.p() + y * row_bytes;
     unsigned char *p = image.p() + y * row_bytes;
@@ -8271,6 +8350,26 @@ read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header, int n
   return image;
   return image;
 }
 }
 
 
+/**
+ * Called by read_dds for a DDS file that needs no transformations applied.
+ */
+PTA_uchar Texture::
+read_dds_level_raw(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
+  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
+  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
+
+  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
+  size_t row_bytes = x_size * cdata->_num_components * cdata->_component_width;
+  nassertr(row_bytes * y_size == size, PTA_uchar());
+  PTA_uchar image = PTA_uchar::empty_array(size);
+  for (int y = y_size - 1; y >= 0; --y) {
+    unsigned char *p = image.p() + y * row_bytes;
+    in.read((char *)p, row_bytes);
+  }
+
+  return image;
+}
+
 /**
 /**
  * Called by read_dds for a DDS file whose format isn't one we've specifically
  * Called by read_dds for a DDS file whose format isn't one we've specifically
  * optimized.
  * optimized.
@@ -8929,13 +9028,20 @@ compare_images(const PNMImage &a, const PNMImage &b) {
   nassertr(a.get_x_size() == b.get_x_size() &&
   nassertr(a.get_x_size() == b.get_x_size() &&
            a.get_y_size() == b.get_y_size(), false);
            a.get_y_size() == b.get_y_size(), false);
 
 
+  const xel *a_array = a.get_array();
+  const xel *b_array = b.get_array();
+  const xelval *a_alpha = a.get_alpha_array();
+  const xelval *b_alpha = b.get_alpha_array();
+
+  int x_size = a.get_x_size();
+
   int delta = 0;
   int delta = 0;
   for (int yi = 0; yi < a.get_y_size(); ++yi) {
   for (int yi = 0; yi < a.get_y_size(); ++yi) {
-    xel *a_row = a.row(yi);
-    xel *b_row = b.row(yi);
-    xelval *a_alpha_row = a.alpha_row(yi);
-    xelval *b_alpha_row = b.alpha_row(yi);
-    for (int xi = 0; xi < a.get_x_size(); ++xi) {
+    const xel *a_row = a_array + yi * x_size;
+    const xel *b_row = b_array + yi * x_size;
+    const xelval *a_alpha_row = a_alpha + yi * x_size;
+    const xelval *b_alpha_row = b_alpha + yi * x_size;
+    for (int xi = 0; xi < x_size; ++xi) {
       delta += abs(PPM_GETR(a_row[xi]) - PPM_GETR(b_row[xi]));
       delta += abs(PPM_GETR(a_row[xi]) - PPM_GETR(b_row[xi]));
       delta += abs(PPM_GETG(a_row[xi]) - PPM_GETG(b_row[xi]));
       delta += abs(PPM_GETG(a_row[xi]) - PPM_GETG(b_row[xi]));
       delta += abs(PPM_GETB(a_row[xi]) - PPM_GETB(b_row[xi]));
       delta += abs(PPM_GETB(a_row[xi]) - PPM_GETB(b_row[xi]));
@@ -10126,6 +10232,14 @@ do_fillin_body(CData *cdata, DatagramIterator &scan, BamReader *manager) {
     cdata->_simple_image_date_generated = scan.get_int32();
     cdata->_simple_image_date_generated = scan.get_int32();
 
 
     size_t u_size = scan.get_uint32();
     size_t u_size = scan.get_uint32();
+
+    // Protect against large allocation.
+    if (u_size > scan.get_remaining_size()) {
+      gobj_cat.error()
+        << "simple RAM image extends past end of datagram, is texture corrupt?\n";
+      return;
+    }
+
     PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
     PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
     scan.extract_bytes(image.p(), u_size);
     scan.extract_bytes(image.p(), u_size);
 
 
@@ -10180,6 +10294,14 @@ do_fillin_rawdata(CData *cdata, DatagramIterator &scan, BamReader *manager) {
 
 
     // fill the cdata->_image buffer with image data
     // fill the cdata->_image buffer with image data
     size_t u_size = scan.get_uint32();
     size_t u_size = scan.get_uint32();
+
+    // Protect against large allocation.
+    if (u_size > scan.get_remaining_size()) {
+      gobj_cat.error()
+        << "RAM image " << n << " extends past end of datagram, is texture corrupt?\n";
+      return;
+    }
+
     PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
     PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
     scan.extract_bytes(image.p(), u_size);
     scan.extract_bytes(image.p(), u_size);
 
 

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

@@ -817,6 +817,8 @@ private:
                                          int n, istream &in);
                                          int n, istream &in);
   static PTA_uchar read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header,
   static PTA_uchar read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header,
                                          int n, istream &in);
                                          int n, istream &in);
+  static PTA_uchar read_dds_level_raw(Texture *tex, CData *cdata, const DDSHeader &header,
+                                      int n, istream &in);
   static PTA_uchar read_dds_level_generic_uncompressed(Texture *tex, CData *cdata,
   static PTA_uchar read_dds_level_generic_uncompressed(Texture *tex, CData *cdata,
                                                        const DDSHeader &header,
                                                        const DDSHeader &header,
                                                        int n, istream &in);
                                                        int n, istream &in);

+ 1 - 1
panda/src/grutil/config_grutil.cxx

@@ -131,6 +131,6 @@ init_libgrutil() {
   MovieTexture::register_with_read_factory();
   MovieTexture::register_with_read_factory();
 
 
   TexturePool *ts = TexturePool::get_global_ptr();
   TexturePool *ts = TexturePool::get_global_ptr();
-  ts->register_texture_type(MovieTexture::make_texture, "avi m2v mov mpg mpeg mp4 wmv asf flv nut ogm mkv ogv");
+  ts->register_texture_type(MovieTexture::make_texture, "avi m2v mov mpg mpeg mp4 wmv asf flv nut ogm mkv ogv webm");
 #endif
 #endif
 }
 }

+ 5 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -31,6 +31,7 @@ class GraphicsOutputBase;
 
 
 class VertexBufferContext;
 class VertexBufferContext;
 class IndexBufferContext;
 class IndexBufferContext;
+class BufferContext;
 class GeomContext;
 class GeomContext;
 class GeomNode;
 class GeomNode;
 class Geom;
 class Geom;
@@ -57,6 +58,7 @@ class SamplerContext;
 class SamplerState;
 class SamplerState;
 class Shader;
 class Shader;
 class ShaderContext;
 class ShaderContext;
+class ShaderBuffer;
 class RenderState;
 class RenderState;
 class TransformState;
 class TransformState;
 class Material;
 class Material;
@@ -162,6 +164,9 @@ public:
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0;
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0;
   virtual void release_index_buffer(IndexBufferContext *ibc)=0;
   virtual void release_index_buffer(IndexBufferContext *ibc)=0;
 
 
+  virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data)=0;
+  virtual void release_shader_buffer(BufferContext *ibc)=0;
+
   virtual void dispatch_compute(int size_x, int size_y, int size_z)=0;
   virtual void dispatch_compute(int size_x, int size_y, int size_z)=0;
 
 
   virtual PT(GeomMunger) get_geom_munger(const RenderState *state,
   virtual PT(GeomMunger) get_geom_munger(const RenderState *state,

+ 6 - 2
panda/src/movies/dr_flac.h

@@ -534,7 +534,9 @@ static DRFLAC_INLINE uint64_t drflac__swap_endian_uint64(uint64_t n)
 
 
 static DRFLAC_INLINE uint32_t drflac__be2host_32(uint32_t n)
 static DRFLAC_INLINE uint32_t drflac__be2host_32(uint32_t n)
 {
 {
-#ifdef __linux__
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER == __ORDER_LITTLE_ENDIAN__)
+    return drflac__swap_endian_uint32(n);
+#elif defined(__linux__)
     return be32toh(n);
     return be32toh(n);
 #else
 #else
     if (drflac__is_little_endian()) {
     if (drflac__is_little_endian()) {
@@ -547,7 +549,9 @@ static DRFLAC_INLINE uint32_t drflac__be2host_32(uint32_t n)
 
 
 static DRFLAC_INLINE uint64_t drflac__be2host_64(uint64_t n)
 static DRFLAC_INLINE uint64_t drflac__be2host_64(uint64_t n)
 {
 {
-#ifdef __linux__
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER == __ORDER_LITTLE_ENDIAN__)
+    return drflac__swap_endian_uint64(n);
+#elif defined(__linux__)
     return be64toh(n);
     return be64toh(n);
 #else
 #else
     if (drflac__is_little_endian()) {
     if (drflac__is_little_endian()) {

+ 1 - 0
panda/src/pgraph/light.h

@@ -90,6 +90,7 @@ protected:
     CP_point_priority,
     CP_point_priority,
     CP_directional_priority,
     CP_directional_priority,
     CP_spot_priority,
     CP_spot_priority,
+    CP_area_priority,
   };
   };
 
 
 private:
 private:

+ 8 - 0
panda/src/pgraph/nodePath.I

@@ -1253,6 +1253,14 @@ set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z
   set_shader_input(new ShaderInput(id, tex, read, write, z, n, priority));
   set_shader_input(new ShaderInput(id, tex, read, write, z, n, priority));
 }
 }
 
 
+/**
+ *
+ */
+INLINE void NodePath::
+set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority) {
+  set_shader_input(new ShaderInput(id, buf, priority));
+}
+
 /**
 /**
  *
  *
  */
  */

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

@@ -61,6 +61,7 @@ class GlobPattern;
 class PreparedGraphicsObjects;
 class PreparedGraphicsObjects;
 class SamplerState;
 class SamplerState;
 class Shader;
 class Shader;
+class ShaderBuffer;
 class ShaderInput;
 class ShaderInput;
 
 
 //
 //
@@ -632,6 +633,7 @@ PUBLISHED:
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
+  INLINE void set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const PTA_double &v, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const PTA_double &v, int priority=0);

+ 32 - 2
panda/src/pgraph/shaderAttrib.cxx

@@ -25,6 +25,7 @@
 #include "datagram.h"
 #include "datagram.h"
 #include "datagramIterator.h"
 #include "datagramIterator.h"
 #include "nodePath.h"
 #include "nodePath.h"
+#include "shaderBuffer.h"
 
 
 TypeHandle ShaderAttrib::_type_handle;
 TypeHandle ShaderAttrib::_type_handle;
 int ShaderAttrib::_attrib_slot;
 int ShaderAttrib::_attrib_slot;
@@ -429,7 +430,8 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const {
       nassertr(!np.is_empty(), LMatrix4::ident_mat());
       nassertr(!np.is_empty(), LMatrix4::ident_mat());
       return np.get_transform()->get_mat();
       return np.get_transform()->get_mat();
 
 
-    } else if (p->get_value_type() == ShaderInput::M_numeric && p->get_ptr()._size == 16) {
+    } else if (p->get_value_type() == ShaderInput::M_numeric &&
+               p->get_ptr()._size >= 16 && (p->get_ptr()._size & 15) == 0) {
       const Shader::ShaderPtrData &ptr = p->get_ptr();
       const Shader::ShaderPtrData &ptr = p->get_ptr();
 
 
       switch (ptr._type) {
       switch (ptr._type) {
@@ -455,12 +457,40 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const {
     }
     }
 
 
     ostringstream strm;
     ostringstream strm;
-    strm << "Shader input " << id->get_name() << " is not a NodePath or LMatrix4.\n";
+    strm << "Shader input " << id->get_name() << " is not a NodePath, LMatrix4 or PTA_LMatrix4.\n";
     nassert_raise(strm.str());
     nassert_raise(strm.str());
     return LMatrix4::ident_mat();
     return LMatrix4::ident_mat();
   }
   }
 }
 }
 
 
+/**
+ * Returns the ShaderInput as a ShaderBuffer.  Assertion fails if there is
+ * none, or if it is not a ShaderBuffer.
+ */
+ShaderBuffer *ShaderAttrib::
+get_shader_input_buffer(const InternalName *id) const {
+  Inputs::const_iterator i = _inputs.find(id);
+  if (i == _inputs.end()) {
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not present.\n";
+    nassert_raise(strm.str());
+    return NULL;
+  } else {
+    const ShaderInput *p = (*i).second;
+
+    if (p->get_value_type() == ShaderInput::M_buffer) {
+      ShaderBuffer *value;
+      DCAST_INTO_R(value, p->_value, NULL);
+      return value;
+    }
+
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not a ShaderBuffer.\n";
+    nassert_raise(strm.str());
+    return NULL;
+  }
+}
+
 /**
 /**
  * Returns the shader object associated with the node.  If get_override
  * Returns the shader object associated with the node.  If get_override
  * returns true, but get_shader returns NULL, that means that this attribute
  * returns true, but get_shader returns NULL, that means that this attribute

+ 1 - 0
panda/src/pgraph/shaderAttrib.h

@@ -111,6 +111,7 @@ PUBLISHED:
   Texture *get_shader_input_texture(const InternalName *id, SamplerState *sampler=NULL) const;
   Texture *get_shader_input_texture(const InternalName *id, SamplerState *sampler=NULL) const;
   const Shader::ShaderPtrData *get_shader_input_ptr(const InternalName *id) const;
   const Shader::ShaderPtrData *get_shader_input_ptr(const InternalName *id) const;
   const LMatrix4 &get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const;
   const LMatrix4 &get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const;
+  ShaderBuffer *get_shader_input_buffer(const InternalName *id) const;
 
 
   static void register_with_read_factory();
   static void register_with_read_factory();
 
 

+ 12 - 0
panda/src/pgraph/shaderInput.I

@@ -55,6 +55,18 @@ ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority) :
 {
 {
 }
 }
 
 
+/**
+ *
+ */
+INLINE ShaderInput::
+ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority) :
+  _name(MOVE(name)),
+  _type(M_buffer),
+  _priority(priority),
+  _value(buf)
+{
+}
+
 /**
 /**
  *
  *
  */
  */

+ 6 - 1
panda/src/pgraph/shaderInput.h

@@ -31,6 +31,7 @@
 #include "samplerState.h"
 #include "samplerState.h"
 #include "shader.h"
 #include "shader.h"
 #include "texture.h"
 #include "texture.h"
+#include "shaderBuffer.h"
 
 
 /**
 /**
  * This is a small container class that can hold any one of the value types
  * This is a small container class that can hold any one of the value types
@@ -52,6 +53,7 @@ PUBLISHED:
   INLINE ShaderInput(CPT_InternalName name, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0);
+  INLINE ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority=0);
@@ -96,7 +98,8 @@ PUBLISHED:
     M_numeric,
     M_numeric,
     M_texture_sampler,
     M_texture_sampler,
     M_param,
     M_param,
-    M_texture_image
+    M_texture_image,
+    M_buffer,
   };
   };
 
 
   INLINE const InternalName *get_name() const;
   INLINE const InternalName *get_name() const;
@@ -123,6 +126,8 @@ private:
   int _priority;
   int _priority;
   int _type;
   int _type;
 
 
+  friend class ShaderAttrib;
+
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;

+ 3 - 0
panda/src/pgraphnodes/config_pgraphnodes.cxx

@@ -26,6 +26,7 @@
 #include "lodNode.h"
 #include "lodNode.h"
 #include "nodeCullCallbackData.h"
 #include "nodeCullCallbackData.h"
 #include "pointLight.h"
 #include "pointLight.h"
+#include "rectangleLight.h"
 #include "selectiveChildNode.h"
 #include "selectiveChildNode.h"
 #include "sequenceNode.h"
 #include "sequenceNode.h"
 #include "shaderGenerator.h"
 #include "shaderGenerator.h"
@@ -121,6 +122,7 @@ init_libpgraphnodes() {
   LODNode::init_type();
   LODNode::init_type();
   NodeCullCallbackData::init_type();
   NodeCullCallbackData::init_type();
   PointLight::init_type();
   PointLight::init_type();
+  RectangleLight::init_type();
   SelectiveChildNode::init_type();
   SelectiveChildNode::init_type();
   SequenceNode::init_type();
   SequenceNode::init_type();
   ShaderGenerator::init_type();
   ShaderGenerator::init_type();
@@ -137,6 +139,7 @@ init_libpgraphnodes() {
   LightNode::register_with_read_factory();
   LightNode::register_with_read_factory();
   LODNode::register_with_read_factory();
   LODNode::register_with_read_factory();
   PointLight::register_with_read_factory();
   PointLight::register_with_read_factory();
+  RectangleLight::register_with_read_factory();
   SelectiveChildNode::register_with_read_factory();
   SelectiveChildNode::register_with_read_factory();
   SequenceNode::register_with_read_factory();
   SequenceNode::register_with_read_factory();
   SphereLight::register_with_read_factory();
   SphereLight::register_with_read_factory();

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

@@ -1,5 +1,6 @@
 #include "nodeCullCallbackData.cxx"
 #include "nodeCullCallbackData.cxx"
 #include "pointLight.cxx"
 #include "pointLight.cxx"
+#include "rectangleLight.cxx"
 #include "sceneGraphAnalyzer.cxx"
 #include "sceneGraphAnalyzer.cxx"
 #include "selectiveChildNode.cxx"
 #include "selectiveChildNode.cxx"
 #include "sequenceNode.cxx"
 #include "sequenceNode.cxx"

+ 59 - 0
panda/src/pgraphnodes/rectangleLight.I

@@ -0,0 +1,59 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file rectangleLight.I
+ * @author rdb
+ * @date 2016-12-19
+ */
+
+/**
+ *
+ */
+INLINE RectangleLight::CData::
+CData() :
+  _max_distance(make_inf((PN_stdfloat)0))
+{
+}
+
+/**
+ *
+ */
+INLINE RectangleLight::CData::
+CData(const RectangleLight::CData &copy) :
+  _max_distance(copy._max_distance)
+{
+}
+
+/**
+ * Returns the color of specular highlights generated by the light.  This is
+ * usually the same as get_color().
+ */
+INLINE const LColor &RectangleLight::
+get_specular_color() const {
+  return get_color();
+}
+
+/**
+ * Returns the maximum distance at which the light has any effect, as previously
+ * specified by set_max_distance.
+ */
+INLINE PN_stdfloat RectangleLight::
+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 RectangleLight::
+set_max_distance(PN_stdfloat max_distance) {
+  CDWriter cdata(_cycler);
+  cdata->_max_distance = max_distance;
+}

+ 150 - 0
panda/src/pgraphnodes/rectangleLight.cxx

@@ -0,0 +1,150 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file rectangleLight.cxx
+ * @author rdb
+ * @date 2016-12-19
+ */
+
+#include "rectangleLight.h"
+#include "graphicsStateGuardianBase.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle RectangleLight::_type_handle;
+
+/**
+ *
+ */
+CycleData *RectangleLight::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void RectangleLight::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  dg.add_stdfloat(_max_distance);
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new Light.
+ */
+void RectangleLight::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  _max_distance = scan.get_stdfloat();
+}
+
+/**
+ *
+ */
+RectangleLight::
+RectangleLight(const string &name) :
+  LightLensNode(name)
+{
+}
+
+/**
+ * Do not call the copy constructor directly; instead, use make_copy() or
+ * copy_subgraph() to make a copy of a node.
+ */
+RectangleLight::
+RectangleLight(const RectangleLight &copy) :
+  LightLensNode(copy),
+  _cycler(copy._cycler)
+{
+}
+
+/**
+ * Returns a newly-allocated PandaNode that is a shallow copy of this one.  It
+ * will be a different pointer, but its internal data may or may not be shared
+ * with that of the original PandaNode.  No children will be copied.
+ */
+PandaNode *RectangleLight::
+make_copy() const {
+  return new RectangleLight(*this);
+}
+
+/**
+ *
+ */
+void RectangleLight::
+write(ostream &out, int indent_level) const {
+  LightLensNode::write(out, indent_level);
+  indent(out, indent_level) << *this << "\n";
+}
+
+/**
+ * Returns the relative priority associated with all lights of this class.
+ * This priority is used to order lights whose instance priority
+ * (get_priority()) is the same--the idea is that other things being equal,
+ * AmbientLights (for instance) are less important than DirectionalLights.
+ */
+int RectangleLight::
+get_class_priority() const {
+  return (int)CP_area_priority;
+}
+
+/**
+ *
+ */
+void RectangleLight::
+bind(GraphicsStateGuardianBase *gsg, const NodePath &light, int light_id) {
+}
+
+/**
+ * Tells the BamReader how to create objects of type RectangleLight.
+ */
+void RectangleLight::
+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 RectangleLight::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  LightLensNode::write_datagram(manager, dg);
+  manager->write_cdata(dg, _cycler);
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type RectangleLight is encountered in the Bam file.  It should create the
+ * RectangleLight and extract its information from the file.
+ */
+TypedWritable *RectangleLight::
+make_from_bam(const FactoryParams &params) {
+  RectangleLight *node = new RectangleLight("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new RectangleLight.
+ */
+void RectangleLight::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  LightLensNode::fillin(scan, manager);
+
+  manager->read_cdata(scan, _cycler);
+}

+ 98 - 0
panda/src/pgraphnodes/rectangleLight.h

@@ -0,0 +1,98 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file rectangleLight.h
+ * @author rdb
+ * @date 2016-12-19
+ */
+
+#ifndef RECTANGLELIGHT_H
+#define RECTANGLELIGHT_H
+
+#include "pandabase.h"
+
+#include "lightLensNode.h"
+#include "pointLight.h"
+
+/**
+ * This is a type of area light that is an axis aligned rectangle, pointing
+ * along the Y axis in the positive direction.
+ */
+class EXPCL_PANDA_PGRAPHNODES RectangleLight : public LightLensNode {
+PUBLISHED:
+  RectangleLight(const string &name);
+
+protected:
+  RectangleLight(const RectangleLight &copy);
+
+public:
+  virtual PandaNode *make_copy() const;
+  virtual void write(ostream &out, int indent_level) const;
+
+PUBLISHED:
+  INLINE const LColor &get_specular_color() const FINAL;
+
+  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;
+
+public:
+  virtual void bind(GraphicsStateGuardianBase *gsg, const NodePath &light,
+                    int light_id);
+
+private:
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA_PGRAPHNODES CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+    virtual TypeHandle get_parent_type() const {
+      return RectangleLight::get_class_type();
+    }
+
+    PN_stdfloat _max_distance;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+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:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    LightLensNode::init_type();
+    register_type(_type_handle, "RectangleLight",
+                  LightLensNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "rectangleLight.I"
+
+#endif

+ 18 - 0
panda/src/pipeline/pythonThread.cxx

@@ -37,6 +37,13 @@ PythonThread(PyObject *function, PyObject *args,
   }
   }
 
 
   set_args(args);
   set_args(args);
+
+#ifndef SIMPLE_THREADS
+  // Ensure that the Python threading system is initialized and ready to go.
+#ifdef WITH_THREAD  // This symbol defined within Python.h
+  PyEval_InitThreads();
+#endif
+#endif
 }
 }
 
 
 /**
 /**
@@ -44,9 +51,20 @@ PythonThread(PyObject *function, PyObject *args,
  */
  */
 PythonThread::
 PythonThread::
 ~PythonThread() {
 ~PythonThread() {
+  // Unfortunately, we need to grab the GIL to release these things,
+  // since the destructor could be called from any thread.
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyGILState_STATE gstate;
+  gstate = PyGILState_Ensure();
+#endif
+
   Py_DECREF(_function);
   Py_DECREF(_function);
   Py_XDECREF(_args);
   Py_XDECREF(_args);
   Py_XDECREF(_result);
   Py_XDECREF(_result);
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyGILState_Release(gstate);
+#endif
 }
 }
 
 
 /**
 /**

+ 2 - 2
panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx

@@ -1046,13 +1046,13 @@ write_data(xel *array, xelval *alpha) {
       bytesperrow = _x_size * samplesperpixel;
       bytesperrow = _x_size * samplesperpixel;
     } else if ( grayscale ) {
     } else if ( grayscale ) {
       samplesperpixel = 1;
       samplesperpixel = 1;
-      bitspersample = pm_maxvaltobits( _maxval );
+      bitspersample = min(8, pm_maxvaltobits(_maxval));
       photometric = PHOTOMETRIC_MINISBLACK;
       photometric = PHOTOMETRIC_MINISBLACK;
       i = 8 / bitspersample;
       i = 8 / bitspersample;
       bytesperrow = ( _x_size + i - 1 ) / i;
       bytesperrow = ( _x_size + i - 1 ) / i;
     } else {
     } else {
       samplesperpixel = 1;
       samplesperpixel = 1;
-      bitspersample = 8;
+      bitspersample = min(8, pm_maxvaltobits(_maxval));
       photometric = PHOTOMETRIC_PALETTE;
       photometric = PHOTOMETRIC_PALETTE;
       bytesperrow = _x_size;
       bytesperrow = _x_size;
     }
     }

+ 3 - 3
panda/src/pstatclient/pStatProperties.cxx

@@ -151,9 +151,9 @@ static TimeCollectorProperties time_properties[] = {
 
 
 static LevelCollectorProperties level_properties[] = {
 static LevelCollectorProperties level_properties[] = {
   { 1, "Graphics memory",                  { 0.0, 0.0, 1.0 },  "MB", 64, 1048576 },
   { 1, "Graphics memory",                  { 0.0, 0.0, 1.0 },  "MB", 64, 1048576 },
-  { 1, "Vertex buffer switch",             { 0.0, 0.6, 0.8 },  "", 500 },
-  { 1, "Vertex buffer switch:Vertex",      { 0.8, 0.0, 0.6 } },
-  { 1, "Vertex buffer switch:Index",       { 0.8, 0.6, 0.3 } },
+  { 1, "Buffer switch",                    { 0.0, 0.6, 0.8 },  "", 500 },
+  { 1, "Buffer switch:Vertex",             { 0.8, 0.0, 0.6 } },
+  { 1, "Buffer switch:Index",              { 0.8, 0.6, 0.3 } },
   { 1, "Geom cache size",                  { 0.6, 0.8, 0.6 },  "", 500 },
   { 1, "Geom cache size",                  { 0.6, 0.8, 0.6 },  "", 500 },
   { 1, "Geom cache size:Active",           { 0.9, 1.0, 0.3 },  "", 500 },
   { 1, "Geom cache size:Active",           { 0.9, 1.0, 0.3 },  "", 500 },
   { 1, "Geom cache operations",            { 1.0, 0.6, 0.6 },  "", 500 },
   { 1, "Geom cache operations",            { 1.0, 0.6, 0.6 },  "", 500 },

+ 16 - 3
panda/src/putil/cachedTypedWritableReferenceCount.I

@@ -126,7 +126,7 @@ cache_ref() const {
 #endif
 #endif
 
 
   ref();
   ref();
-  AtomicAdjust::inc(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count);
+  AtomicAdjust::inc(_cache_ref_count);
 }
 }
 
 
 /**
 /**
@@ -147,7 +147,7 @@ cache_unref() const {
   // you can't use PointerTo's?
   // you can't use PointerTo's?
   nassertr(_cache_ref_count > 0, 0);
   nassertr(_cache_ref_count > 0, 0);
 
 
-  AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count);
+  AtomicAdjust::dec(_cache_ref_count);
   return ReferenceCount::unref();
   return ReferenceCount::unref();
 }
 }
 
 
@@ -164,6 +164,19 @@ test_ref_count_integrity() const {
 #endif
 #endif
 }
 }
 
 
+/**
+ * Decrements the cache reference count without affecting the normal reference
+ * count.  Don't use this.
+ */
+INLINE void CachedTypedWritableReferenceCount::
+cache_ref_only() const {
+#ifdef _DEBUG
+  nassertv(test_ref_count_integrity());
+#endif
+
+  AtomicAdjust::inc(_cache_ref_count);
+}
+
 /**
 /**
  * Decrements the cache reference count without affecting the normal reference
  * Decrements the cache reference count without affecting the normal reference
  * count.  Intended to be called by derived classes only, presumably to
  * count.  Intended to be called by derived classes only, presumably to
@@ -180,7 +193,7 @@ cache_unref_only() const {
   // you can't use PointerTo's?
   // you can't use PointerTo's?
   nassertv(_cache_ref_count > 0);
   nassertv(_cache_ref_count > 0);
 
 
-  AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count);
+  AtomicAdjust::dec(_cache_ref_count);
 }
 }
 
 
 /**
 /**

+ 4 - 1
panda/src/putil/cachedTypedWritableReferenceCount.h

@@ -46,12 +46,15 @@ PUBLISHED:
 
 
   MAKE_PROPERTY(cache_ref_count, get_cache_ref_count);
   MAKE_PROPERTY(cache_ref_count, get_cache_ref_count);
 
 
+public:
+  INLINE void cache_ref_only() const;
+
 protected:
 protected:
   INLINE void cache_unref_only() const;
   INLINE void cache_unref_only() const;
   bool do_test_ref_count_integrity() const;
   bool do_test_ref_count_integrity() const;
 
 
 private:
 private:
-  AtomicAdjust::Integer _cache_ref_count;
+  mutable AtomicAdjust::Integer _cache_ref_count;
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 17 - 0
panda/src/putil/copyOnWriteObject.cxx

@@ -35,4 +35,21 @@ unref() const {
   }
   }
   return is_zero;
   return is_zero;
 }
 }
+
+/**
+ * Explicitly increments the cache reference count only.  Don't use this.
+ *
+ * In the case of a CopyOnWriteObject, when the reference count decrements
+ * down to the cache reference count, the object is implicitly unlocked.
+ */
+void CopyOnWriteObject::
+cache_ref_only() const {
+  MutexHolder holder(_lock_mutex);
+  CachedTypedWritableReferenceCount::cache_ref_only();
+  if (get_ref_count() == get_cache_ref_count()) {
+    ((CopyOnWriteObject *)this)->_lock_status = LS_unlocked;
+    ((CopyOnWriteObject *)this)->_locking_thread = NULL;
+    ((CopyOnWriteObject *)this)->_lock_cvar.notify();
+  }
+}
 #endif  // COW_THREADED
 #endif  // COW_THREADED

+ 3 - 0
panda/src/putil/copyOnWriteObject.h

@@ -49,6 +49,9 @@ PUBLISHED:
   virtual bool unref() const;
   virtual bool unref() const;
   INLINE void cache_ref() const;
   INLINE void cache_ref() const;
   INLINE bool cache_unref() const;
   INLINE bool cache_unref() const;
+
+public:
+  void cache_ref_only() const;
 #endif  // COW_THREADED
 #endif  // COW_THREADED
 
 
 protected:
 protected:

+ 86 - 11
panda/src/putil/copyOnWritePointer.I

@@ -74,23 +74,58 @@ INLINE CopyOnWritePointer::
  *
  *
  */
  */
 INLINE CopyOnWritePointer::
 INLINE CopyOnWritePointer::
-CopyOnWritePointer(CopyOnWritePointer &&move) NOEXCEPT :
-  _cow_object(move._cow_object)
+CopyOnWritePointer(CopyOnWritePointer &&from) NOEXCEPT :
+  _cow_object(from._cow_object)
 {
 {
   // Steal the other's reference count.
   // Steal the other's reference count.
-  move._cow_object = (CopyOnWriteObject *)NULL;
+  from._cow_object = (CopyOnWriteObject *)NULL;
+}
+
+/**
+ *
+ */
+INLINE CopyOnWritePointer::
+CopyOnWritePointer(PointerTo<CopyOnWriteObject> &&from) NOEXCEPT :
+  _cow_object(from.p())
+{
+  // Steal the other's reference count, but because it is a regular pointer,
+  // we do need to include the cache reference count.
+  if (_cow_object != (CopyOnWriteObject *)NULL) {
+    _cow_object->cache_ref_only();
+  }
+  from.cheat() = NULL;
 }
 }
 
 
 /**
 /**
  *
  *
  */
  */
 INLINE void CopyOnWritePointer::
 INLINE void CopyOnWritePointer::
-operator = (CopyOnWritePointer &&move) NOEXCEPT {
+operator = (CopyOnWritePointer &&from) NOEXCEPT {
   // Protect against self-move-assignment.
   // Protect against self-move-assignment.
-  if (move._cow_object != _cow_object) {
+  if (from._cow_object != _cow_object) {
     CopyOnWriteObject *old_object = _cow_object;
     CopyOnWriteObject *old_object = _cow_object;
-    _cow_object = move._cow_object;
-    move._cow_object = NULL;
+    _cow_object = from._cow_object;
+    from._cow_object = NULL;
+
+    if (old_object != (CopyOnWriteObject *)NULL) {
+      cache_unref_delete(old_object);
+    }
+  }
+}
+
+/**
+ *
+ */
+INLINE void CopyOnWritePointer::
+operator = (PointerTo<CopyOnWriteObject> &&from) NOEXCEPT {
+  if (from.p() != _cow_object) {
+    CopyOnWriteObject *old_object = _cow_object;
+
+    // Steal the other's reference count, but because it is a regular pointer,
+    // we do need to include the cache reference count.
+    _cow_object = from.p();
+    _cow_object->cache_ref_only();
+    from.cheat() = NULL;
 
 
     if (old_object != (CopyOnWriteObject *)NULL) {
     if (old_object != (CopyOnWriteObject *)NULL) {
       cache_unref_delete(old_object);
       cache_unref_delete(old_object);
@@ -262,20 +297,60 @@ operator = (To *object) {
  */
  */
 template<class T>
 template<class T>
 INLINE CopyOnWritePointerTo<T>::
 INLINE CopyOnWritePointerTo<T>::
-CopyOnWritePointerTo(CopyOnWritePointerTo<T> &&move) NOEXCEPT :
-  CopyOnWritePointer((CopyOnWritePointer &&)move)
+CopyOnWritePointerTo(CopyOnWritePointerTo<T> &&from) NOEXCEPT :
+  CopyOnWritePointer((CopyOnWritePointer &&)from)
 {
 {
 }
 }
 #endif  // CPPPARSER
 #endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
+/**
+ *
+ */
+template<class T>
+INLINE CopyOnWritePointerTo<T>::
+CopyOnWritePointerTo(PointerTo<T> &&from) NOEXCEPT {
+  // Steal the other's reference count, but because it is a regular pointer,
+  // we do need to include the cache reference count.
+  _cow_object = from.p();
+  if (_cow_object != (CopyOnWriteObject *)NULL) {
+    _cow_object->cache_ref_only();
+  }
+  from.cheat() = NULL;
+}
+#endif  // CPPPARSER
+
+#ifndef CPPPARSER
+/**
+ *
+ */
+template<class T>
+INLINE void CopyOnWritePointerTo<T>::
+operator = (CopyOnWritePointerTo<T> &&from) NOEXCEPT {
+  CopyOnWritePointer::operator = ((CopyOnWritePointer &&)from);
+}
+#endif  // CPPPARSER
+
 #ifndef CPPPARSER
 #ifndef CPPPARSER
 /**
 /**
  *
  *
  */
  */
 template<class T>
 template<class T>
 INLINE void CopyOnWritePointerTo<T>::
 INLINE void CopyOnWritePointerTo<T>::
-operator = (CopyOnWritePointerTo<T> &&move) NOEXCEPT {
-  CopyOnWritePointer::operator = ((CopyOnWritePointer &&)move);
+operator = (PointerTo<T> &&from) NOEXCEPT {
+  if (from.p() != _cow_object) {
+    CopyOnWriteObject *old_object = _cow_object;
+
+    // Steal the other's reference count, but because it is a regular pointer,
+    // we do need to include the cache reference count.
+    _cow_object = from.p();
+    _cow_object->cache_ref_only();
+    from.cheat() = NULL;
+
+    if (old_object != (CopyOnWriteObject *)NULL) {
+      cache_unref_delete(old_object);
+    }
+  }
 }
 }
 #endif  // CPPPARSER
 #endif  // CPPPARSER
 #endif  // USE_MOVE_SEMANTICS
 #endif  // USE_MOVE_SEMANTICS

+ 12 - 16
panda/src/putil/copyOnWritePointer.cxx

@@ -90,19 +90,17 @@ get_write_pointer() {
     }
     }
 
 
     PT(CopyOnWriteObject) new_object = _cow_object->make_cow_copy();
     PT(CopyOnWriteObject) new_object = _cow_object->make_cow_copy();
+    _cow_object->CachedTypedWritableReferenceCount::cache_unref();
+    _cow_object->_lock_mutex.release();
 
 
-    // We can't call cache_unref_delete, because we hold the lock.
-    if (!_cow_object->CachedTypedWritableReferenceCount::cache_unref()) {
-      _cow_object->_lock_mutex.release();
-      delete _cow_object;
-    } else {
-      _cow_object->_lock_mutex.release();
-    }
+    MutexHolder holder(new_object->_lock_mutex);
     _cow_object = new_object;
     _cow_object = new_object;
-    _cow_object->cache_ref();
+    _cow_object->CachedTypedWritableReferenceCount::cache_ref();
     _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write;
     _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write;
     _cow_object->_locking_thread = current_thread;
     _cow_object->_locking_thread = current_thread;
 
 
+    return new_object;
+
   } else if (_cow_object->get_cache_ref_count() > 1) {
   } else if (_cow_object->get_cache_ref_count() > 1) {
     // No one else has it specifically read-locked, but there are other
     // No one else has it specifically read-locked, but there are other
     // CopyOnWritePointers holding the same object, so we should make our own
     // CopyOnWritePointers holding the same object, so we should make our own
@@ -115,19 +113,17 @@ get_write_pointer() {
     }
     }
 
 
     PT(CopyOnWriteObject) new_object = _cow_object->make_cow_copy();
     PT(CopyOnWriteObject) new_object = _cow_object->make_cow_copy();
+    _cow_object->CachedTypedWritableReferenceCount::cache_unref();
+    _cow_object->_lock_mutex.release();
 
 
-    // We can't call cache_unref_delete, because we hold the lock.
-    if (!_cow_object->CachedTypedWritableReferenceCount::cache_unref()) {
-      _cow_object->_lock_mutex.release();
-      delete _cow_object;
-    } else {
-      _cow_object->_lock_mutex.release();
-    }
+    MutexHolder holder(new_object->_lock_mutex);
     _cow_object = new_object;
     _cow_object = new_object;
-    _cow_object->cache_ref();
+    _cow_object->CachedTypedWritableReferenceCount::cache_ref();
     _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write;
     _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write;
     _cow_object->_locking_thread = current_thread;
     _cow_object->_locking_thread = current_thread;
 
 
+    return new_object;
+
   } else {
   } else {
     // No other thread has the pointer locked, and we're the only
     // No other thread has the pointer locked, and we're the only
     // CopyOnWritePointer with this object.  We can safely write to it without
     // CopyOnWritePointer with this object.  We can safely write to it without

+ 9 - 5
panda/src/putil/copyOnWritePointer.h

@@ -37,8 +37,10 @@ public:
   INLINE ~CopyOnWritePointer();
   INLINE ~CopyOnWritePointer();
 
 
 #ifdef USE_MOVE_SEMANTICS
 #ifdef USE_MOVE_SEMANTICS
-  INLINE CopyOnWritePointer(CopyOnWritePointer &&move) NOEXCEPT;
-  INLINE void operator = (CopyOnWritePointer &&move) NOEXCEPT;
+  INLINE CopyOnWritePointer(CopyOnWritePointer &&from) NOEXCEPT;
+  INLINE CopyOnWritePointer(PointerTo<CopyOnWriteObject> &&from) NOEXCEPT;
+  INLINE void operator = (CopyOnWritePointer &&from) NOEXCEPT;
+  INLINE void operator = (PointerTo<CopyOnWriteObject> &&from) NOEXCEPT;
 #endif
 #endif
 
 
   INLINE bool operator == (const CopyOnWritePointer &other) const;
   INLINE bool operator == (const CopyOnWritePointer &other) const;
@@ -61,7 +63,7 @@ public:
   INLINE bool test_ref_count_integrity() const;
   INLINE bool test_ref_count_integrity() const;
   INLINE bool test_ref_count_nonzero() const;
   INLINE bool test_ref_count_nonzero() const;
 
 
-private:
+protected:
   CopyOnWriteObject *_cow_object;
   CopyOnWriteObject *_cow_object;
 };
 };
 
 
@@ -84,8 +86,10 @@ public:
   INLINE void operator = (To *object);
   INLINE void operator = (To *object);
 
 
 #ifdef USE_MOVE_SEMANTICS
 #ifdef USE_MOVE_SEMANTICS
-  INLINE CopyOnWritePointerTo(CopyOnWritePointerTo &&move) NOEXCEPT;
-  INLINE void operator = (CopyOnWritePointerTo &&move) NOEXCEPT;
+  INLINE CopyOnWritePointerTo(CopyOnWritePointerTo &&from) NOEXCEPT;
+  INLINE CopyOnWritePointerTo(PointerTo<T> &&from) NOEXCEPT;
+  INLINE void operator = (CopyOnWritePointerTo &&from) NOEXCEPT;
+  INLINE void operator = (PointerTo<T> &&from) NOEXCEPT;
 #endif
 #endif
 
 
 #ifdef COW_THREADED
 #ifdef COW_THREADED

+ 16 - 0
panda/src/vision/openCVTexture.cxx

@@ -21,6 +21,22 @@
 #include "bamReader.h"
 #include "bamReader.h"
 #include "bamCacheRecord.h"
 #include "bamCacheRecord.h"
 
 
+// This symbol is predefined by the Panda3D build system to select whether we
+// are using the OpenCV 2.3 or later interface, or if it is not defined, we
+// are using the original interface.
+#ifdef OPENCV_VER_23
+
+#include <opencv2/core/core.hpp>
+// #include <opencv2videovideo.hpp>
+#include <opencv2/highgui/highgui.hpp>
+
+#else
+#include <cv.h>
+#include <cxcore.h>
+#include <highgui.h>
+
+#endif  // OPENCV_VER_23
+
 TypeHandle OpenCVTexture::_type_handle;
 TypeHandle OpenCVTexture::_type_handle;
 
 
 /**
 /**

Some files were not shown because too many files changed in this diff