Browse Source

Merge branch 'master' of https://github.com/panda3d/panda3d

David Rose 9 years ago
parent
commit
0b224a320a
69 changed files with 752 additions and 283 deletions
  1. 5 3
      .travis.yml
  2. 1 1
      direct/src/directscripts/eggcacher.py
  3. 1 1
      direct/src/directscripts/packpanda.py
  4. 2 2
      direct/src/p3d/Packager.py
  5. 1 1
      direct/src/p3d/PatchMaker.py
  6. 3 2
      direct/src/plugin/p3dPackage.cxx
  7. 221 83
      direct/src/showutil/FreezeTool.py
  8. 19 4
      direct/src/showutil/pfreeze.py
  9. 2 2
      dtool/src/parser-inc/zlib.h
  10. 8 3
      makepanda/makepanda.py
  11. 1 1
      panda/metalibs/pandaegg/pandaegg.h
  12. 29 10
      panda/src/audio/audioManager.cxx
  13. 2 0
      panda/src/audiotraits/config_openalAudio.cxx
  14. 1 1
      panda/src/audiotraits/config_openalAudio.h
  15. 1 1
      panda/src/audiotraits/milesAudioManager.cxx
  16. 1 1
      panda/src/collada/loaderFileTypeDae.cxx
  17. 1 1
      panda/src/downloader/decompressor.cxx
  18. 1 1
      panda/src/egg2pg/loaderFileTypeEgg.cxx
  19. 7 3
      panda/src/express/virtualFileSimple.cxx
  20. 3 3
      panda/src/express/virtualFileSystem.I
  21. 2 2
      panda/src/express/zStreamBuf.cxx
  22. 6 2
      panda/src/framework/pandaFramework.cxx
  23. 1 1
      panda/src/framework/windowFramework.cxx
  24. 19 0
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  25. 11 2
      panda/src/glstuff/glShaderContext_src.cxx
  26. 4 1
      panda/src/gobj/geomVertexArrayFormat.cxx
  27. 12 2
      panda/src/gobj/geomVertexColumn.cxx
  28. 23 14
      panda/src/gobj/material.cxx
  29. 11 7
      panda/src/gobj/matrixLens.cxx
  30. 32 0
      panda/src/gobj/shader.I
  31. 1 0
      panda/src/gobj/shader.h
  32. 2 2
      panda/src/gobj/texture.I
  33. 46 8
      panda/src/gobj/texture.cxx
  34. 1 0
      panda/src/gobj/texture.h
  35. 4 1
      panda/src/gobj/textureStage.cxx
  36. 4 1
      panda/src/grutil/movieTexture.cxx
  37. 11 9
      panda/src/grutil/shaderTerrainMesh.I
  38. 28 34
      panda/src/grutil/shaderTerrainMesh.cxx
  39. 5 6
      panda/src/grutil/shaderTerrainMesh.h
  40. 3 3
      panda/src/movies/dr_flac.h
  41. 18 4
      panda/src/pgraph/clipPlaneAttrib.cxx
  42. 4 2
      panda/src/pgraph/depthOffsetAttrib.cxx
  43. 7 3
      panda/src/pgraph/light.cxx
  44. 17 4
      panda/src/pgraph/lightAttrib.cxx
  45. 8 8
      panda/src/pgraph/loader.cxx
  46. 1 1
      panda/src/pgraph/loaderFileType.cxx
  47. 1 1
      panda/src/pgraph/loaderFileTypeBam.cxx
  48. 9 2
      panda/src/pgraph/occluderEffect.cxx
  49. 12 1
      panda/src/pgraph/paramNodePath.cxx
  50. 3 1
      panda/src/pgraph/scissorAttrib.cxx
  51. 20 2
      panda/src/pgraph/stencilAttrib.cxx
  52. 4 1
      panda/src/pgraph/texMatrixAttrib.cxx
  53. 10 4
      panda/src/pgraph/textureAttrib.cxx
  54. 3 1
      panda/src/pgraphnodes/directionalLight.cxx
  55. 3 1
      panda/src/pgraphnodes/pointLight.cxx
  56. 3 1
      panda/src/pgraphnodes/spotlight.cxx
  57. 6 2
      panda/src/pgraphnodes/uvScrollNode.cxx
  58. 1 1
      panda/src/pnmimage/pnmFileTypeRegistry.cxx
  59. 3 2
      panda/src/pnmimage/pnmImageHeader.cxx
  60. 10 0
      panda/src/putil/bamWriter.I
  61. 48 7
      panda/src/putil/bamWriter.cxx
  62. 1 0
      panda/src/putil/bamWriter.h
  63. 6 0
      panda/src/putil/config_util.cxx
  64. 1 0
      panda/src/putil/config_util.h
  65. 1 1
      panda/src/speedtree/loaderFileTypeSrt.cxx
  66. 1 1
      panda/src/speedtree/loaderFileTypeStf.cxx
  67. 1 1
      pandatool/src/assimp/loaderFileTypeAssimp.cxx
  68. 1 1
      pandatool/src/ptloader/loaderFileTypePandatool.cxx
  69. 13 11
      samples/shader-terrain/main.py

+ 5 - 3
.travis.yml

@@ -3,9 +3,11 @@ sudo: false
 matrix:
   include:
     - compiler: gcc
-      env: PYTHONV=python2.7
+      env: PYTHONV=python2.7 FLAGS=
     - compiler: clang
-      env: PYTHONV=python3
+      env: PYTHONV=python3 FLAGS=--installer
+    - compiler: clang
+      env: PYTHONV=python2.7 FLAGS=--override=STDFLOAT_DOUBLE=1
 addons:
   apt:
     packages:
@@ -26,7 +28,7 @@ addons:
     - python-dev
     - python3-dev
     - zlib1g-dev
-script: $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT --installer --threads 4 && LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py
+script: $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4 && LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py
 notifications:
   irc:
     channels:

+ 1 - 1
direct/src/directscripts/eggcacher.py

@@ -55,7 +55,7 @@ class EggCacher:
             size = os.path.getsize(path)
             eggs.append((path,size))
             return
-        if (path.endswith(".egg.pz")):
+        if (path.endswith(".egg.pz") or path.endswith(".egg.gz")):
             size = os.path.getsize(path)
             if (self.pzkeep): eggs.append((path,size))
             else: eggs.append((path[:-3],size))

+ 1 - 1
direct/src/directscripts/packpanda.py

@@ -274,7 +274,7 @@ def CompileFiles(file):
     if (os.path.isfile(file)):
         if (file.endswith(".egg")):
             egg2bam(file, file[:-4]+'.bam')
-        elif (file.endswith(".egg.pz")):
+        elif (file.endswith(".egg.pz") or file.endswith(".egg.gz")):
             egg2bam(file, file[:-7]+'.bam')
         elif (file.endswith(".py")):
             py2pyc(file)

+ 2 - 2
direct/src/p3d/Packager.py

@@ -61,7 +61,7 @@ class Packager:
                 self.newName = str(self.filename)
 
             ext = Filename(self.newName).getExtension()
-            if ext == 'pz':
+            if ext == 'pz' or ext == 'gz':
                 # Strip off a .pz extension; we can compress files
                 # within the Multifile without it.
                 filename = Filename(self.newName)
@@ -3772,7 +3772,7 @@ class Packager:
             self.currentPackage.addFile(filename, newName = newName,
                                         explicit = False, unprocessed = unprocessed)
         else:
-            if ext == 'pz':
+            if ext == 'pz' or ext == 'gz':
                 # Strip off an implicit .pz extension.
                 newFilename = Filename(filename)
                 newFilename.setExtension('')

+ 1 - 1
direct/src/p3d/PatchMaker.py

@@ -137,7 +137,7 @@ class PatchMaker:
 
             startFile, startPv, plan = self.getRecreateFilePlan()
 
-            if startFile.getExtension() == 'pz':
+            if startFile.getExtension() in ('pz', 'gz'):
                 # If the starting file is compressed, we have to
                 # decompress it first.
                 assert startPv.tempFile is None

+ 3 - 2
direct/src/plugin/p3dPackage.cxx

@@ -998,7 +998,8 @@ build_install_plans(TiXmlDocument *doc) {
           FileSpec new_file = patchfile->_file;
           string new_filename = new_file.get_filename();
           size_t dot = new_filename.rfind('.');
-          assert(new_filename.substr(dot) == ".pz");
+          string extension = new_filename.substr(dot);
+          assert(extension == ".pz" || extension == ".gz");
           new_filename = new_filename.substr(0, dot);
           new_file.set_filename(new_filename);
           step = new InstallStepUncompressFile
@@ -1834,7 +1835,7 @@ thread_step() {
   z.next_in = (Bytef *)decompress_buffer;
   z.avail_in = (size_t)read_count;
 
-  int result = inflateInit(&z);
+  int result = inflateInit2(&z, 32 + 15);
   if (result < 0) {
     nout << z.msg << "\n";
     return IT_step_failed;

+ 221 - 83
direct/src/showutil/FreezeTool.py

@@ -8,7 +8,7 @@ import marshal
 import imp
 import platform
 from io import StringIO
-from distutils.sysconfig import PREFIX, get_python_inc, get_python_version, get_config_var
+import distutils.sysconfig as sysconf
 
 # Temporary (?) try..except to protect against unbuilt p3extend_frozen.
 try:
@@ -29,9 +29,23 @@ isDebugBuild = (python.lower().endswith('_d'))
 # These are modules that Python always tries to import up-front.  They
 # must be frozen in any main.exe.
 startupModules = [
-    'os', 'encodings.cp1252',
-    'encodings.latin_1', 'encodings.utf_8', 'io',
+    'encodings.cp1252', 'encodings.latin_1', 'encodings.utf_8',
     ]
+if sys.version_info >= (3, 0):
+    startupModules += ['io', 'marshal', 'importlib.machinery', 'importlib.util']
+
+# These are some special init functions for some built-in Python modules that
+# deviate from the standard naming convention.  A value of None means that a
+# dummy entry should be written to the inittab.
+builtinInitFuncs = {
+    'builtins': None,
+    '__builtin__': None,
+    'sys': None,
+    'exceptions': None,
+    '_imp': 'PyInit_imp',
+    '_warnings': '_PyWarnings_Init',
+    'marshal': 'PyMarshal_Init',
+}
 
 # These are missing modules that we've reported already this session.
 reportedMissing = {}
@@ -60,8 +74,8 @@ class CompilationEnvironment:
 
         # Paths to Python stuff.
         self.Python = None
-        self.PythonIPath = get_python_inc()
-        self.PythonVersion = get_config_var("LDVERSION") or get_python_version()
+        self.PythonIPath = sysconf.get_python_inc()
+        self.PythonVersion = sysconf.get_config_var("LDVERSION") or sysconf.get_python_version()
 
         # The VC directory of Microsoft Visual Studio (if relevant)
         self.MSVC = None
@@ -85,7 +99,7 @@ class CompilationEnvironment:
 
     def determineStandardSetup(self):
         if self.platform.startswith('win'):
-            self.Python = PREFIX
+            self.Python = sysconf.PREFIX
 
             if ('VCINSTALLDIR' in os.environ):
                 self.MSVC = os.environ['VCINSTALLDIR']
@@ -122,13 +136,15 @@ class CompilationEnvironment:
 
             # If it is run by makepanda, it handles the MSVC and PlatformSDK paths itself.
             if ('MAKEPANDA' in os.environ):
-                self.compileObj = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" %(filename)s'
+                self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" %(filename)s'
+                self.compileObjDll = self.compileObjExe
                 self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(python)s\libs"  /out:%(basename)s.exe %(basename)s.obj'
                 self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(python)s\libs"  /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
             else:
                 os.environ['PATH'] += ';' + self.MSVC + '\\bin' + self.suffix64 + ';' + self.MSVC + '\\Common7\\IDE;' + self.PSDK + '\\bin'
 
-                self.compileObj = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" /I"%(PSDK)s\include" /I"%(MSVC)s\include" %(filename)s'
+                self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" /I"%(PSDK)s\include" /I"%(MSVC)s\include" %(filename)s'
+                self.compileObjDll = self.compileObjExe
                 self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\libs"  /out:%(basename)s.exe %(basename)s.obj'
                 self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\libs"  /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
 
@@ -141,22 +157,26 @@ class CompilationEnvironment:
                 self.arch = '-arch ppc'
             elif proc == 'amd64':
                 self.arch = '-arch x86_64'
-            self.compileObj = "gcc -fPIC -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
+            self.compileObjExe = "gcc -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
+            self.compileObjDll = "gcc -fPIC -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
             self.linkExe = "gcc %(arch)s -o %(basename)s %(basename)s.o -framework Python"
             self.linkDll = "gcc %(arch)s -undefined dynamic_lookup -bundle -o %(basename)s.so %(basename)s.o"
 
         else:
             # Unix
-            self.compileObj = "gcc -fPIC -c -o %(basename)s.o -O2 %(filename)s -I%(pythonIPath)s"
-            self.linkExe = "gcc -o %(basename)s %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
-            self.linkDll = "gcc -shared -o %(basename)s.so %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
+            lib_dir = sysconf.get_python_lib(plat_specific=1, standard_lib=1)
+            #python_a = os.path.join(lib_dir, "config", "libpython%(pythonVersion)s.a")
+            self.compileObjExe = "%(CC)s %(CFLAGS)s -c -o %(basename)s.o -pthread -O2 %(filename)s -I%(pythonIPath)s"
+            self.compileObjDll = "%(CC)s %(CFLAGS)s %(CCSHARED)s -c -o %(basename)s.o -O2 %(filename)s -I%(pythonIPath)s"
+            self.linkExe = "%(CC)s -o %(basename)s %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
+            self.linkDll = "%(LDSHARED)s -o %(basename)s.so %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
 
             if (os.path.isdir("/usr/PCBSD/local/lib")):
                 self.linkExe += " -L/usr/PCBSD/local/lib"
                 self.linkDll += " -L/usr/PCBSD/local/lib"
 
-    def compileExe(self, filename, basename):
-        compile = self.compileObj % {
+    def compileExe(self, filename, basename, extraLink=[]):
+        compile = self.compileObjExe % dict({
             'python' : self.Python,
             'MSVC' : self.MSVC,
             'PSDK' : self.PSDK,
@@ -167,12 +187,12 @@ class CompilationEnvironment:
             'arch' : self.arch,
             'filename' : filename,
             'basename' : basename,
-            }
+            }, **sysconf.get_config_vars())
         sys.stderr.write(compile + '\n')
         if os.system(compile) != 0:
             raise Exception('failed to compile %s.' % basename)
 
-        link = self.linkExe % {
+        link = self.linkExe % dict({
             'python' : self.Python,
             'MSVC' : self.MSVC,
             'PSDK' : self.PSDK,
@@ -182,13 +202,14 @@ class CompilationEnvironment:
             'arch' : self.arch,
             'filename' : filename,
             'basename' : basename,
-            }
+            }, **sysconf.get_config_vars())
+        link += ' ' + ' '.join(extraLink)
         sys.stderr.write(link + '\n')
         if os.system(link) != 0:
             raise Exception('failed to link %s.' % basename)
 
-    def compileDll(self, filename, basename):
-        compile = self.compileObj % {
+    def compileDll(self, filename, basename, extraLink=[]):
+        compile = self.compileObjDll % dict({
             'python' : self.Python,
             'MSVC' : self.MSVC,
             'PSDK' : self.PSDK,
@@ -199,12 +220,12 @@ class CompilationEnvironment:
             'arch' : self.arch,
             'filename' : filename,
             'basename' : basename,
-            }
+            }, **sysconf.get_config_vars())
         sys.stderr.write(compile + '\n')
         if os.system(compile) != 0:
             raise Exception('failed to compile %s.' % basename)
 
-        link = self.linkDll % {
+        link = self.linkDll % dict({
             'python' : self.Python,
             'MSVC' : self.MSVC,
             'PSDK' : self.PSDK,
@@ -215,7 +236,8 @@ class CompilationEnvironment:
             'filename' : filename,
             'basename' : basename,
             'dllext' : self.dllext,
-            }
+            }, **sysconf.get_config_vars())
+        link += ' ' + ' '.join(extraLink)
         sys.stderr.write(link + '\n')
         if os.system(link) != 0:
             raise Exception('failed to link %s.' % basename)
@@ -233,7 +255,8 @@ frozenMainCode = """
 #ifdef MS_WINDOWS
 extern void PyWinFreeze_ExeInit(void);
 extern void PyWinFreeze_ExeTerm(void);
-extern int PyInitFrozenExtensions(void);
+
+extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
 #endif
 
 /* Main program */
@@ -264,6 +287,8 @@ Py_FrozenMain(int argc, char **argv)
 #endif
 
     Py_FrozenFlag = 1; /* Suppress errors from getpath.c */
+    Py_NoSiteFlag = 1;
+    Py_NoUserSiteDirectory = 1;
 
     if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\\0')
         inspect = 1;
@@ -300,7 +325,7 @@ Py_FrozenMain(int argc, char **argv)
 #endif
 
 #ifdef MS_WINDOWS
-    PyInitFrozenExtensions();
+    PyImport_ExtendInittab(extensions);
 #endif /* MS_WINDOWS */
 
     if (argc >= 1) {
@@ -344,8 +369,8 @@ Py_FrozenMain(int argc, char **argv)
 #endif
     Py_Finalize();
 
-error:
 #if PY_MAJOR_VERSION >= 3
+error:
     PyMem_RawFree(argv_copy);
     if (argv_copy2) {
         for (i = 0; i < argc; i++)
@@ -463,10 +488,6 @@ main(int argc, char *argv[]) {
 
 # Our own glue code to start up a Python shared library.
 dllInitCode = """
-static PyMethodDef nullMethods[] = {
-  {NULL, NULL}
-};
-
 /*
  * Call this function to extend the frozen modules array with a new
  * array of frozen modules, provided in a C-style array, at runtime.
@@ -508,10 +529,29 @@ extend_frozen_modules(const struct _frozen *new_modules, int new_count) {
   return orig_count + new_count;
 }
 
-%(dllexport)svoid init%(moduleName)s() {
+#if PY_MAJOR_VERSION >= 3
+static PyModuleDef mdef = {
+  PyModuleDef_HEAD_INIT,
+  "%(moduleName)s",
+  "",
+  -1,
+  NULL, NULL, NULL, NULL, NULL
+};
+
+%(dllexport)sPyObject *PyInit_%(moduleName)s(void) {
+  extend_frozen_modules(_PyImport_FrozenModules, sizeof(_PyImport_FrozenModules) / sizeof(struct _frozen));
+  return PyModule_Create(&mdef);
+}
+#else
+static PyMethodDef nullMethods[] = {
+  {NULL, NULL}
+};
+
+%(dllexport)svoid init%(moduleName)s(void) {
   extend_frozen_modules(_PyImport_FrozenModules, sizeof(_PyImport_FrozenModules) / sizeof(struct _frozen));
   Py_InitModule("%(moduleName)s", nullMethods);
 }
+#endif
 """
 
 programFile = """
@@ -526,24 +566,8 @@ struct _frozen _PyImport_FrozenModules[] = {
 %(moduleList)s
   {NULL, NULL, 0}
 };
-
-%(initCode)s
 """
 
-# Windows needs this bit.
-frozenExtensions = """
-
-static struct _inittab extensions[] = {
-        /* Sentinel */
-        {0, 0}
-};
-extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
-
-int PyInitFrozenExtensions()
-{
-        return PyImport_ExtendInittab(extensions);
-}
-"""
 
 okMissing = [
     '__main__', '_dummy_threading', 'Carbon', 'Carbon.Files',
@@ -645,14 +669,13 @@ class Freezer:
         if self.platform.startswith('win'):
             self.objectExtension = '.obj'
 
-        self.keepTemporaryFiles = True
+        self.keepTemporaryFiles = False
 
         # Change any of these to change the generated startup and glue
         # code.
         self.frozenMainCode = frozenMainCode
         self.frozenDllMainCode = frozenDllMainCode
         self.mainInitCode = mainInitCode
-        self.frozenExtensions = frozenExtensions
 
         # Set this true to encode Python files in a Multifile as their
         # original source if possible, or false to encode them as
@@ -664,9 +687,14 @@ class Freezer:
         # addToMultifile().  It contains a list of all the extension
         # modules that were discovered, which have not been added to
         # the output.  The list is a list of tuples of the form
-        # (moduleName, filename).
+        # (moduleName, filename).  filename will be None for built-in
+        # modules.
         self.extras = []
 
+        # Set this to true if extension modules should be linked in to
+        # the resulting executable.
+        self.linkExtensionModules = False
+
         # End of public interface.  These remaining members should not
         # be directly manipulated by callers.
         self.previousModules = {}
@@ -676,6 +704,10 @@ class Freezer:
             self.previousModules = dict(previous.modules)
             self.modules = dict(previous.modules)
 
+        # Exclude doctest by default; it is not very useful in production
+        # builds.  It can be explicitly included if desired.
+        self.modules['doctest'] = self.ModuleDef('doctest', exclude = True)
+
         self.mf = None
 
         # Actually, make sure we know how to find all of the
@@ -951,8 +983,8 @@ class Freezer:
         for mdef in includes:
             try:
                 self.__loadModule(mdef)
-            except ImportError:
-                print("Unknown module: %s" % (mdef.moduleName))
+            except ImportError as ex:
+                print("Unknown module: %s (%s)" % (mdef.moduleName, str(ex)))
 
         # Also attempt to import any implicit modules.  If any of
         # these fail to import, we don't really care.
@@ -1296,41 +1328,116 @@ class Freezer:
             if mdef.forbid:
                 # Explicitly disallow importing this module.
                 moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
-            else:
-                assert not mdef.exclude
-                # Allow importing this module.
-                module = self.mf.modules.get(origName, None)
-                code = getattr(module, "__code__", None)
-                if code:
-                    code = marshal.dumps(code)
+                continue
 
-                    mangledName = self.mangleName(moduleName)
-                    moduleDefs.append(self.makeModuleDef(mangledName, code))
-                    moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
+            assert not mdef.exclude
+            # Allow importing this module.
+            module = self.mf.modules.get(origName, None)
+            code = getattr(module, "__code__", None)
+            if code:
+                code = marshal.dumps(code)
 
-                elif moduleName in startupModules:
-                    # Forbid the loading of this startup module.
-                    moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
+                mangledName = self.mangleName(moduleName)
+                moduleDefs.append(self.makeModuleDef(mangledName, code))
+                moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
+                continue
 
-                else:
-                    # This is a module with no associated Python
-                    # code.  It must be an extension module.  Get the
-                    # filename.
-                    extensionFilename = getattr(module, '__file__', None)
-                    if extensionFilename:
-                        self.extras.append((moduleName, extensionFilename))
-                    else:
-                        # It doesn't even have a filename; it must
-                        # be a built-in module.  No worries about
-                        # this one, then.
-                        pass
+            #if moduleName in startupModules:
+            #    # Forbid the loading of this startup module.
+            #    moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
+            #    continue
+
+            # This is a module with no associated Python code.  It is either
+            # an extension module or a builtin module.  Get the filename, if
+            # it is the former.
+            extensionFilename = getattr(module, '__file__', None)
+
+            if extensionFilename or self.linkExtensionModules:
+                self.extras.append((moduleName, extensionFilename))
+
+            # If it is a submodule of a frozen module, Python will have
+            # trouble importing it as a builtin module.  Synthesize a frozen
+            # module that loads it as builtin.
+            if '.' in moduleName and self.linkExtensionModules:
+                code = compile('import sys;del sys.modules["%s"];import imp;imp.init_builtin("%s")' % (moduleName, moduleName), moduleName, 'exec')
+                code = marshal.dumps(code)
+                mangledName = self.mangleName(moduleName)
+                moduleDefs.append(self.makeModuleDef(mangledName, code))
+                moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, None))
+            elif '.' in moduleName:
+                # Nothing we can do about this case except warn the user they
+                # are in for some trouble.
+                print('WARNING: Python cannot import extension modules under '
+                      'frozen Python packages; %s will be inaccessible.  '
+                      'passing either -l to link in extension modules or use '
+                      '-x %s to exclude the entire package.' % (moduleName, moduleName.split('.')[0]))
 
         text = programFile % {
             'moduleDefs': '\n'.join(moduleDefs),
             'moduleList': '\n'.join(moduleList),
-            'initCode': initCode
             }
 
+        if self.linkExtensionModules and self.extras:
+            # Should we link in extension modules?  If so, we write out a new
+            # built-in module table that directly hooks up with the init
+            # functions.  On Linux, we completely override Python's own
+            # built-in module table; on Windows, we can't do this, so we
+            # instead use PyImport_ExtendInittab to add to it.
+
+            # Python 3 case.
+            text += '#if PY_MAJOR_VERSION >= 3\n'
+            for module, fn in self.extras:
+                if sys.platform != "win32" or fn:
+                    libName = module.split('.')[-1]
+                    initFunc = builtinInitFuncs.get(module, 'PyInit_' + libName)
+                    if initFunc:
+                        text += 'extern DL_IMPORT(PyObject) *%s(void);\n' % (initFunc)
+            text += '\n'
+
+            if sys.platform == "win32":
+                text += 'static struct _inittab extensions[] = {\n'
+            else:
+                text += 'struct _inittab _PyImport_Inittab[] = {\n'
+
+            for module, fn in self.extras:
+                if sys.platform != "win32" or fn:
+                    libName = module.split('.')[-1]
+                    initFunc = builtinInitFuncs.get(module, 'PyInit_' + libName) or 'NULL'
+                    text += '  {"%s", %s},\n' % (module, initFunc)
+            text += '  {0, 0},\n'
+            text += '};\n\n'
+
+            # Python 2 case.
+            text += '#else\n'
+            for module, fn in self.extras:
+                if sys.platform != "win32" or fn:
+                    libName = module.split('.')[-1]
+                    initFunc = builtinInitFuncs.get(module, 'init' + libName)
+                    if initFunc:
+                        text += 'extern DL_IMPORT(void) %s(void);\n' % (initFunc)
+            text += '\n'
+
+            if sys.platform == "win32":
+                text += 'static struct _inittab extensions[] = {\n'
+            else:
+                text += 'struct _inittab _PyImport_Inittab[] = {\n'
+
+            for module, fn in self.extras:
+                if sys.platform != "win32" or fn:
+                    libName = module.split('.')[-1]
+                    initFunc = builtinInitFuncs.get(module, 'init' + libName) or 'NULL'
+                    text += '  {"%s", %s},\n' % (module, initFunc)
+            text += '  {0, 0},\n'
+            text += '};\n'
+            text += '#endif\n\n'
+
+        elif sys.platform == "win32":
+            text += 'static struct _inittab extensions[] = {\n'
+            text += '  {0, 0},\n'
+            text += '};\n\n'
+
+        text += initCode
+
         if filename is not None:
             file = open(filename, 'w')
             file.write(text)
@@ -1375,7 +1482,6 @@ class Freezer:
                 'dllimport' : dllimport,
                 }
             if self.platform.startswith('win'):
-                initCode += self.frozenExtensions
                 target = basename + '.exe'
             else:
                 target = basename
@@ -1397,14 +1503,46 @@ class Freezer:
 
         self.writeCode(filename, initCode=initCode)
 
+        # Keep track of the files we should clean up after use.
+        cleanFiles = [filename, basename + self.objectExtension]
+
+        extraLink = []
+        if self.linkExtensionModules:
+            for mod, fn in self.extras:
+                if not fn:
+                    continue
+                if sys.platform == 'win32':
+                    # We can't link with a .pyd directly on Windows.  Check
+                    # if there is a corresponding .lib file in the Python libs
+                    # directory.
+                    libsdir = os.path.join(sys.exec_prefix, 'libs')
+                    libfile = os.path.join(libsdir, mod + '.lib')
+                    if os.path.isfile(libfile):
+                        extraLink.append(mod + '.lib')
+                        continue
+
+                    # No, so we have to generate a .lib file.  This is pretty
+                    # easy given that we know the only symbol we need is a
+                    # initmodule or PyInit_module function.
+                    modname = mod.split('.')[-1]
+                    libfile = modname + '.lib'
+                    if sys.version_info >= (3, 0):
+                        symbolName = 'PyInit_' + modname
+                    else:
+                        symbolName = 'init' + modname
+                    os.system('lib /nologo /def /export:%s /name:%s.pyd /out:%s' % (symbolName, modname, libfile))
+                    extraLink.append(libfile)
+                    cleanFiles += [libfile, modname + '.exp']
+                else:
+                    extraLink.append(fn)
+
         try:
-            compileFunc(filename, basename)
+            compileFunc(filename, basename, extraLink=extraLink)
         finally:
             if not self.keepTemporaryFiles:
-                if os.path.exists(filename):
-                    os.unlink(filename)
-                if os.path.exists(basename + self.objectExtension):
-                    os.unlink(basename + self.objectExtension)
+                for file in cleanFiles:
+                    if os.path.exists(file):
+                        os.unlink(file)
 
         return target
 

+ 19 - 4
direct/src/showutil/pfreeze.py

@@ -15,7 +15,7 @@ imported directly or indirectly by the original startfile.py.
 
 Usage:
 
-  pfreeze.py [opts] startfile
+  pfreeze.py [opts] [startfile]
 
 Options:
 
@@ -40,11 +40,20 @@ Options:
      of the __path__ variable, and thus must be actually imported to
      determine the true value of __path__.
 
+  -P path
+     Specifies an additional directory in which we should search for
+     Python modules.  This is equivalent to setting the PYTHONPATH
+     environment variable.  May be repeated.
+
   -s
      Adds the standard set of modules that are necessary for embedding
      the Python interpreter.  Implicitly set if an executable is
      generated.
 
+  -k
+     Keeps temporary files generated by pfreeze.  Useful when debugging
+     FreezeTool itself.
+
 """
 
 import getopt
@@ -55,7 +64,7 @@ from direct.showutil import FreezeTool
 def usage(code, msg = ''):
     if __doc__:
         sys.stderr.write(__doc__ + '\n')
-    sys.stderr.write(msg + '\n')
+    sys.stderr.write(str(msg) + '\n')
     sys.exit(code)
 
 # We're not protecting the next part under a __name__ == __main__
@@ -67,7 +76,7 @@ basename = None
 addStartupModules = False
 
 try:
-    opts, args = getopt.getopt(sys.argv[1:], 'o:i:x:p:sh')
+    opts, args = getopt.getopt(sys.argv[1:], 'o:i:x:p:P:slkh')
 except getopt.error as msg:
     usage(1, msg)
 
@@ -83,8 +92,14 @@ for opt, arg in opts:
     elif opt == '-p':
         for module in arg.split(','):
             freezer.handleCustomPath(module)
+    elif opt == '-P':
+        sys.path.append(arg)
     elif opt == '-s':
         addStartupModules = True
+    elif opt == '-l':
+        freezer.linkExtensionModules = True
+    elif opt == '-k':
+        freezer.keepTemporaryFiles = True
     elif opt == '-h':
         usage(0)
     else:
@@ -126,7 +141,7 @@ if args:
 
 elif outputType == 'exe':
     # We must have a main module when making an executable.
-    usage(0)
+    usage(1, 'A main file needs to be specified when creating an executable.')
 
 freezer.done(addStartupModules = addStartupModules)
 

+ 2 - 2
dtool/src/parser-inc/zlib.h

@@ -21,8 +21,8 @@
 
 #include "zconf.h"
 
-class z_stream {
-};
+typedef struct z_stream_s z_stream;
+typedef struct gz_header_s gz_header;
 
 #endif
 

+ 8 - 3
makepanda/makepanda.py

@@ -592,8 +592,12 @@ if (COMPILER == "MSVC"):
     if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbc32.lib")
     if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbccp32.lib")
     if (PkgSkip("OPENSSL")==0):
-        LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandassl.lib")
-        LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandaeay.lib")
+        if os.path.isfile(GetThirdpartyDir() + "openssl/lib/libpandassl.lib"):
+            LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandassl.lib")
+            LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandaeay.lib")
+        else:
+            LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libeay32.lib")
+            LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/ssleay32.lib")
     if (PkgSkip("PNG")==0):
         if os.path.isfile(GetThirdpartyDir() + "png/lib/libpng16_static.lib"):
             LibName("PNG", GetThirdpartyDir() + "png/lib/libpng16_static.lib")
@@ -4039,7 +4043,8 @@ if (not RUNTIME):
   TargetAdd('core.pyd', input='p3display_pythonGraphicsWindowProc.obj')
 
   TargetAdd('core.pyd', input='core_module.obj')
-  TargetAdd('core.pyd', input='libp3tinyxml.ilb')
+  if not GetLinkAllStatic() and GetTarget() != 'emscripten':
+     TargetAdd('core.pyd', input='libp3tinyxml.ilb')
   TargetAdd('core.pyd', input='libp3interrogatedb.dll')
   TargetAdd('core.pyd', input=COMMON_PANDA_LIBS)
   TargetAdd('core.pyd', opts=['PYTHON', 'WINSOCK2'])

+ 1 - 1
panda/metalibs/pandaegg/pandaegg.h

@@ -9,6 +9,6 @@
 
 #include "pandabase.h"
 
-EXPCL_PANDAEGG void init_libpandaegg();
+extern "C" EXPCL_PANDAEGG void init_libpandaegg();
 
 #endif

+ 29 - 10
panda/src/audio/audioManager.cxx

@@ -36,25 +36,35 @@ namespace {
   }
 }
 
-Create_AudioManager_proc* AudioManager::_create_AudioManager
-    =create_NullAudioManager;
+Create_AudioManager_proc *AudioManager::_create_AudioManager = NULL;
 
-void AudioManager::register_AudioManager_creator(Create_AudioManager_proc* proc) {
-  nassertv(_create_AudioManager==create_NullAudioManager);
-  _create_AudioManager=proc;
+void AudioManager::
+register_AudioManager_creator(Create_AudioManager_proc* proc) {
+  nassertv(_create_AudioManager == NULL);
+  _create_AudioManager = proc;
 }
 
-
-
 // Factory method for getting a platform specific AudioManager:
 PT(AudioManager) AudioManager::create_AudioManager() {
   audio_debug("create_AudioManager()\n  audio_library_name=\""<<audio_library_name<<"\"");
+
+  if (_create_AudioManager != NULL) {
+    // Someone was already so good as to register an audio manager creation function,
+    // perhaps by statically linking the requested library.  Let's use that, then.
+    PT(AudioManager) am = (*_create_AudioManager)();
+    if (!am->is_exact_type(NullAudioManager::get_class_type()) && !am->is_valid()) {
+      audio_error("  " << am->get_type() << " is not valid, will use NullAudioManager");
+      am = create_NullAudioManager();
+    }
+    return am;
+  }
+
   static bool lib_load = false;
   if (!lib_load) {
     lib_load = true;
-    if (!audio_library_name.empty() && !(audio_library_name == "null")) {
+    if (!audio_library_name.empty() && audio_library_name != "null") {
       Filename dl_name = Filename::dso_filename(
-          "lib"+string(audio_library_name)+".so");
+          "lib" + string(audio_library_name) + ".so");
       dl_name.to_os_specific();
       audio_debug("  dl_name=\""<<dl_name<<"\"");
       void *handle = load_dso(get_plugin_path().get_value(), dl_name);
@@ -84,11 +94,20 @@ PT(AudioManager) AudioManager::create_AudioManager() {
         } else {
           typedef Create_AudioManager_proc *FuncType();
           Create_AudioManager_proc *factory_func = (*(FuncType *)dso_symbol)();
-          AudioManager::register_AudioManager_creator(factory_func);
+
+          // Note that the audio manager module may register itself upon load.
+          if (_create_AudioManager == NULL) {
+            AudioManager::register_AudioManager_creator(factory_func);
+          }
         }
       }
     }
   }
+
+  if (_create_AudioManager == NULL) {
+    _create_AudioManager = create_NullAudioManager;
+  }
+
   PT(AudioManager) am = (*_create_AudioManager)();
   if (!am->is_exact_type(NullAudioManager::get_class_type()) && !am->is_valid()) {
     audio_error("  " << am->get_type() << " is not valid, will use NullAudioManager");

+ 2 - 0
panda/src/audiotraits/config_openalAudio.cxx

@@ -48,6 +48,8 @@ init_libOpenALAudio() {
   OpenALAudioManager::init_type();
   OpenALAudioSound::init_type();
 
+  AudioManager::register_AudioManager_creator(&Create_OpenALAudioManager);
+
   PandaSystem *ps = PandaSystem::get_global_ptr();
   ps->add_system("OpenAL");
   ps->add_system("audio");

+ 1 - 1
panda/src/audiotraits/config_openalAudio.h

@@ -22,7 +22,7 @@
 ConfigureDecl(config_openalAudio, EXPCL_OPENAL_AUDIO, EXPTP_OPENAL_AUDIO);
 NotifyCategoryDecl(openalAudio, EXPCL_OPENAL_AUDIO, EXPTP_OPENAL_AUDIO);
 
-extern EXPCL_OPENAL_AUDIO void init_libOpenALAudio();
+extern "C" EXPCL_OPENAL_AUDIO void init_libOpenALAudio();
 extern "C" EXPCL_OPENAL_AUDIO Create_AudioManager_proc *get_audio_manager_func_openal_audio();
 
 extern ConfigVariableString openal_device;

+ 1 - 1
panda/src/audiotraits/milesAudioManager.cxx

@@ -893,7 +893,7 @@ load(const Filename &file_name) {
   sd->_basename = file_name.get_basename();
 
   string extension = sd->_basename.get_extension();
-  if (extension == "pz") {
+  if (extension == "pz" || extension == "gz") {
     extension = Filename(sd->_basename.get_basename_wo_extension()).get_extension();
   }
 

+ 1 - 1
panda/src/collada/loaderFileTypeDae.cxx

@@ -56,7 +56,7 @@ get_additional_extensions() const {
 
 /**
  * Returns true if this file type can transparently load compressed files
- * (with a .pz extension), false otherwise.
+ * (with a .pz or .gz extension), false otherwise.
  */
 bool LoaderFileTypeDae::
 supports_compressed() const {

+ 1 - 1
panda/src/downloader/decompressor.cxx

@@ -55,7 +55,7 @@ Decompressor::
 int Decompressor::
 initiate(const Filename &source_file) {
   string extension = source_file.get_extension();
-  if (extension == "pz") {
+  if (extension == "pz" || extension == "gz") {
     Filename dest_file = source_file;
     dest_file = source_file.get_fullpath_wo_extension();
     return initiate(source_file, dest_file);

+ 1 - 1
panda/src/egg2pg/loaderFileTypeEgg.cxx

@@ -44,7 +44,7 @@ get_extension() const {
 
 /**
  * Returns true if this file type can transparently load compressed files
- * (with a .pz extension), false otherwise.
+ * (with a .pz or .gz extension), false otherwise.
  */
 bool LoaderFileTypeEgg::
 supports_compressed() const {

+ 7 - 3
panda/src/express/virtualFileSimple.cxx

@@ -186,7 +186,7 @@ copy_file(VirtualFile *new_file) {
  * (which you should eventually delete when you are done reading). Returns
  * NULL on failure.
  *
- * If auto_unwrap is true, an explicitly-named .pz file is automatically
+ * If auto_unwrap is true, an explicitly-named .pz/.gz file is automatically
  * decompressed and the decompressed contents are returned.  This is different
  * than vfs-implicit-pz, which will automatically decompress a file if the
  * extension .pz is *not* given.
@@ -195,7 +195,9 @@ istream *VirtualFileSimple::
 open_read_file(bool auto_unwrap) const {
 
   // Will we be automatically unwrapping a .pz file?
-  bool do_uncompress = (_implicit_pz_file || (auto_unwrap && _local_filename.get_extension() == "pz"));
+  bool do_uncompress = (_implicit_pz_file ||
+    (auto_unwrap && (_local_filename.get_extension() == "pz" ||
+                     _local_filename.get_extension() == "gz")));
 
   Filename local_filename(_local_filename);
   if (do_uncompress) {
@@ -364,7 +366,9 @@ bool VirtualFileSimple::
 read_file(pvector<unsigned char> &result, bool auto_unwrap) const {
 
   // Will we be automatically unwrapping a .pz file?
-  bool do_uncompress = (_implicit_pz_file || (auto_unwrap && _local_filename.get_extension() == "pz"));
+  bool do_uncompress = (_implicit_pz_file ||
+    (auto_unwrap && (_local_filename.get_extension() == "pz" ||
+                     _local_filename.get_extension() == "gz")));
 
   Filename local_filename(_local_filename);
   if (do_uncompress) {

+ 3 - 3
panda/src/express/virtualFileSystem.I

@@ -88,7 +88,7 @@ ls_all(const Filename &filename) const {
  * Convenience function; returns the entire contents of the indicated file as
  * a string.
  *
- * If auto_unwrap is true, an explicitly-named .pz file is automatically
+ * If auto_unwrap is true, an explicitly-named .pz/.gz file is automatically
  * decompressed and the decompressed contents are returned.  This is different
  * than vfs-implicit-pz, which will automatically decompress a file if the
  * extension .pz is *not* given.
@@ -118,7 +118,7 @@ write_file(const Filename &filename, const string &data, bool auto_wrap) {
  * file, if it exists and can be read.  Returns true on success, false
  * otherwise.
  *
- * If auto_unwrap is true, an explicitly-named .pz file is automatically
+ * If auto_unwrap is true, an explicitly-named .pz/.gz file is automatically
  * decompressed and the decompressed contents are returned.  This is different
  * than vfs-implicit-pz, which will automatically decompress a file if the
  * extension .pz is *not* given.
@@ -134,7 +134,7 @@ read_file(const Filename &filename, string &result, bool auto_unwrap) const {
  * file, if it exists and can be read.  Returns true on success, false
  * otherwise.
  *
- * If auto_unwrap is true, an explicitly-named .pz file is automatically
+ * If auto_unwrap is true, an explicitly-named .pz/.gz file is automatically
  * decompressed and the decompressed contents are returned.  This is different
  * than vfs-implicit-pz, which will automatically decompress a file if the
  * extension .pz is *not* given.

+ 2 - 2
panda/src/express/zStreamBuf.cxx

@@ -87,9 +87,9 @@ open_read(istream *source, bool owns_source) {
   _z_source.opaque = Z_NULL;
   _z_source.msg = (char *)"no error message";
 
-  int result = inflateInit(&_z_source);
+  int result = inflateInit2(&_z_source, 32 + 15);
   if (result < 0) {
-    show_zlib_error("inflateInit", result, _z_source);
+    show_zlib_error("inflateInit2", result, _z_source);
     close_read();
   }
   thread_consider_yield();

+ 6 - 2
panda/src/framework/pandaFramework.cxx

@@ -31,6 +31,12 @@
 #include "transformState.h"
 #include "renderState.h"
 
+#ifdef LINK_ALL_STATIC
+#ifdef HAVE_EGG
+#include "pandaegg.h"
+#endif
+#endif
+
 LoaderOptions PandaFramework::_loader_options;
 
 /**
@@ -101,10 +107,8 @@ open_framework(int &argc, char **&argv) {
 
   // We also want the egg loader.
   #ifdef HAVE_EGG
-  extern EXPCL_PANDAEGG void init_libpandaegg();
   init_libpandaegg();
   #endif
-
 #endif
 
   // Let's explicitly make a call to the image type library to ensure it gets

+ 1 - 1
panda/src/framework/windowFramework.cxx

@@ -591,7 +591,7 @@ load_model(const NodePath &parent, Filename filename) {
   bool is_image = false;
   string extension = filename.get_extension();
 #ifdef HAVE_ZLIB
-  if (extension == "pz") {
+  if (extension == "pz" || extension == "gz") {
     extension = Filename(filename.get_basename_wo_extension()).get_extension();
   }
 #endif  // HAVE_ZLIB

+ 19 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -8334,6 +8334,7 @@ get_external_image_format(Texture *tex) const {
       case Texture::F_blue:
       case Texture::F_r8i:
       case Texture::F_r16:
+      case Texture::F_r16i:
       case Texture::F_r32:
       case Texture::F_r32i:
         return GL_COMPRESSED_RED;
@@ -8539,6 +8540,7 @@ get_external_image_format(Texture *tex) const {
 
 #ifndef OPENGLES_1
   case Texture::F_r8i:
+  case Texture::F_r16i:
   case Texture::F_r32i:
     return GL_RED_INTEGER;
   case Texture::F_rg8i:
@@ -8596,6 +8598,7 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
       case Texture::F_rg8i:
       case Texture::F_rgb8i:
       case Texture::F_rgba8i:
+      case Texture::F_r16i:
       case Texture::F_r32i:
       case Texture::F_r11_g11_b10:
       case Texture::F_rgb9_e5:
@@ -9072,6 +9075,12 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
     } else {
       return GL_R16_SNORM;
     }
+  case Texture::F_r16i:
+    if (Texture::is_unsigned(tex->get_component_type())) {
+      return GL_R16UI;
+    } else {
+      return GL_R16I;
+    }
   case Texture::F_rg16:
     if (tex->get_component_type() == Texture::T_float) {
       return GL_RG16F;
@@ -12662,6 +12671,16 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
     type = Texture::T_unsigned_byte;
     format = Texture::F_rgba8i;
     break;
+
+  case GL_R16I:
+    type = Texture::T_short;
+    format = Texture::F_r16i;
+    break;
+  case GL_R16UI:
+    type = Texture::T_unsigned_short;
+    format = Texture::F_r16i;
+    break;
+
 #endif
 
 #ifndef OPENGLES_1

+ 11 - 2
panda/src/glstuff/glShaderContext_src.cxx

@@ -2666,10 +2666,10 @@ glsl_report_shader_errors(GLuint shader, Shader::ShaderType type, bool fatal) {
   istringstream log(info_log);
   string line;
   while (getline(log, line)) {
-    int fileno, lineno;
+    int fileno, lineno, colno;
     int prefixlen = 0;
 
-    // First is AMDIntel driver syntax, second is NVIDIA syntax.
+    // This first format is used by the majority of compilers.
     if (sscanf(line.c_str(), "ERROR: %d:%d: %n", &fileno, &lineno, &prefixlen) == 2
         && prefixlen > 0) {
 
@@ -2688,10 +2688,19 @@ glsl_report_shader_errors(GLuint shader, Shader::ShaderType type, bool fatal) {
     } else if (sscanf(line.c_str(), "%d(%d) : %n", &fileno, &lineno, &prefixlen) == 2
                && prefixlen > 0) {
 
+      // This is the format NVIDIA uses.
       Filename fn = _shader->get_filename_from_index(fileno, type);
       GLCAT.error(false)
         << fn << "(" << lineno << ") : " << (line.c_str() + prefixlen) << "\n";
 
+    } else if (sscanf(line.c_str(), "%d:%d(%d): %n", &fileno, &lineno, &colno, &prefixlen) == 3
+               && prefixlen > 0) {
+
+      // This is the format for Mesa's OpenGL ES 2 implementation.
+      Filename fn = _shader->get_filename_from_index(fileno, type);
+      GLCAT.error(false)
+        << fn << ":" << lineno << "(" << colno << "): " << (line.c_str() + prefixlen) << "\n";
+
     } else if (!fatal) {
       GLCAT.warning(false) << line << "\n";
 

+ 4 - 1
panda/src/gobj/geomVertexArrayFormat.cxx

@@ -708,7 +708,10 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   dg.add_uint16(_stride);
   dg.add_uint16(_total_bytes);
   dg.add_uint8(_pad_to);
-  dg.add_uint16(_divisor);
+
+  if (manager->get_file_minor_ver() > 36) {
+    dg.add_uint16(_divisor);
+  }
 
   consider_sort_columns();
 

+ 12 - 2
panda/src/gobj/geomVertexColumn.cxx

@@ -378,9 +378,19 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   manager->write_pointer(dg, _name);
   dg.add_uint8(_num_components);
   dg.add_uint8(_numeric_type);
-  dg.add_uint8(_contents);
+
+  if (_contents == C_normal && manager->get_file_minor_ver() < 38) {
+    // Panda 1.9 did not have C_normal.
+    dg.add_uint8(C_vector);
+  } else {
+    dg.add_uint8(_contents);
+  }
+
   dg.add_uint16(_start);
-  dg.add_uint8(_column_alignment);
+
+  if (manager->get_file_minor_ver() >= 29) {
+    dg.add_uint8(_column_alignment);
+  }
 }
 
 /**

+ 23 - 14
panda/src/gobj/material.cxx

@@ -257,7 +257,7 @@ set_roughness(PN_stdfloat roughness) {
   // Calculate the specular exponent from the roughness as it is used in
   // Blinn-Phong shading model.  We use the popular Disney method of squaring
   // the roughness to get a more perceptually linear scale.  From:
-  // http:graphicrants.blogspot.de201308specular-brdf-reference.html
+  // http://graphicrants.blogspot.de/2013/08/specular-brdf-reference.html
   if (roughness <= 0 || IS_NEARLY_ZERO(roughness)) {
     _shininess = make_inf((PN_stdfloat)0);
   } else {
@@ -481,26 +481,35 @@ void Material::
 write_datagram(BamWriter *manager, Datagram &me) {
   me.add_string(get_name());
 
-  me.add_int32(_flags);
+  if (manager->get_file_minor_ver() >= 39) {
+    me.add_int32(_flags);
 
-  if (_flags & F_metallic) {
-    // Metalness workflow.
-    _base_color.write_datagram(me);
-    me.add_stdfloat(_metallic);
+    if (_flags & F_metallic) {
+      // Metalness workflow.
+      _base_color.write_datagram(me);
+      me.add_stdfloat(_metallic);
+    } else {
+      _ambient.write_datagram(me);
+      _diffuse.write_datagram(me);
+      _specular.write_datagram(me);
+    }
+    _emission.write_datagram(me);
+
+    if (_flags & F_roughness) {
+      me.add_stdfloat(_roughness);
+    } else {
+      me.add_stdfloat(_shininess);
+    }
+
+    me.add_stdfloat(_refractive_index);
   } else {
     _ambient.write_datagram(me);
     _diffuse.write_datagram(me);
     _specular.write_datagram(me);
-  }
-  _emission.write_datagram(me);
-
-  if (_flags & F_roughness) {
-    me.add_stdfloat(_roughness);
-  } else {
+    _emission.write_datagram(me);
     me.add_stdfloat(_shininess);
+    me.add_int32(_flags & 0x7f);
   }
-
-  me.add_stdfloat(_refractive_index);
 }
 
 /**

+ 11 - 7
panda/src/gobj/matrixLens.cxx

@@ -83,14 +83,18 @@ register_with_read_factory() {
  */
 void MatrixLens::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  dg.add_uint8(_ml_flags);
-  _user_mat.write_datagram(dg);
+  Lens::write_datagram(manager, dg);
 
-  if (_ml_flags & MF_has_left_eye) {
-    _left_eye_mat.write_datagram(dg);
-  }
-  if (_ml_flags & MF_has_right_eye) {
-    _left_eye_mat.write_datagram(dg);
+  if (manager->get_file_minor_ver() >= 41) {
+    dg.add_uint8(_ml_flags);
+    _user_mat.write_datagram(dg);
+
+    if (_ml_flags & MF_has_left_eye) {
+      _left_eye_mat.write_datagram(dg);
+    }
+    if (_ml_flags & MF_has_right_eye) {
+      _left_eye_mat.write_datagram(dg);
+    }
   }
 }
 

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

@@ -50,6 +50,38 @@ get_filename(ShaderType type) const {
   }
 }
 
+/**
+ * Sets the Shader's filename for the given shader type.  Useful for
+ * associating a shader created with Shader.make with a name for diagnostics.
+ */
+INLINE void Shader::
+set_filename(ShaderType type, const Filename &filename) {
+  _filename._separate = true;
+  switch (type) {
+  case ST_vertex:
+    _filename._vertex = filename;
+    break;
+  case ST_fragment:
+    _filename._fragment = filename;
+    break;
+  case ST_geometry:
+    _filename._geometry = filename;
+    break;
+  case ST_tess_control:
+    _filename._tess_control = filename;
+    break;
+  case ST_tess_evaluation:
+    _filename._tess_evaluation = filename;
+    break;
+  case ST_compute:
+    _filename._compute = filename;
+    break;
+  default:
+    _filename._shared = filename;
+    _filename._separate = false;
+  }
+}
+
 /**
  * Return the Shader's text for the given shader type.
  */

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

@@ -98,6 +98,7 @@ PUBLISHED:
   static PT(Shader) make_compute(ShaderLanguage lang, const string &body);
 
   INLINE Filename get_filename(ShaderType type = ST_none) const;
+  INLINE void set_filename(ShaderType type, const Filename &filename);
   INLINE const string &get_text(ShaderType type = ST_none) const;
   INLINE bool get_error_flag() const;
   INLINE ShaderLanguage get_language() const;

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

@@ -2325,7 +2325,7 @@ INLINE bool Texture::
 is_txo_filename(const Filename &fullpath) {
   string extension = fullpath.get_extension();
 #ifdef HAVE_ZLIB
-  if (extension == "pz") {
+  if (extension == "pz" || extension == "gz") {
     extension = Filename(fullpath.get_basename_wo_extension()).get_extension();
   }
 #endif  // HAVE_ZLIB
@@ -2340,7 +2340,7 @@ INLINE bool Texture::
 is_dds_filename(const Filename &fullpath) {
   string extension = fullpath.get_extension();
 #ifdef HAVE_ZLIB
-  if (extension == "pz") {
+  if (extension == "pz" || extension == "gz") {
     extension = Filename(fullpath.get_basename_wo_extension()).get_extension();
   }
 #endif  // HAVE_ZLIB

+ 46 - 8
panda/src/gobj/texture.cxx

@@ -514,6 +514,7 @@ estimate_texture_memory() const {
     break;
 
   case Texture::F_r16:
+  case Texture::F_r16i:
   case Texture::F_rg8i:
     bpp = 2;
     break;
@@ -1532,6 +1533,10 @@ write(ostream &out, int indent_level) const {
   case F_r16:
     out << "r16";
     break;
+  case F_r16i:
+    out << "r16i";
+    break;
+
   case F_rg16:
     out << "rg16";
     break;
@@ -1950,6 +1955,8 @@ format_format(Format format) {
     return "rgba32";
   case F_r16:
     return "r16";
+  case F_r16i:
+    return "r16i";  
   case F_rg16:
     return "rg16";
   case F_rgb16:
@@ -2049,6 +2056,8 @@ string_format(const string &str) {
     return F_rgba32;
   } else if (cmp_nocase(str, "r16") == 0 || cmp_nocase(str, "red16") == 0) {
     return F_r16;
+  } else if (cmp_nocase(str, "r16i") == 0) {
+    return F_r16i;
   } else if (cmp_nocase(str, "rg16") == 0 || cmp_nocase(str, "r16g16") == 0) {
     return F_rg16;
   } else if (cmp_nocase(str, "rgb16") == 0 || cmp_nocase(str, "r16g16b16") == 0) {
@@ -5745,6 +5754,7 @@ do_set_format(CData *cdata, Texture::Format format) {
   case F_alpha:
   case F_luminance:
   case F_r16:
+  case F_r16i:
   case F_sluminance:
   case F_r32i:
   case F_r32:
@@ -8663,8 +8673,19 @@ do_write_datagram_header(CData *cdata, BamWriter *manager, Datagram &me, bool &h
   me.add_uint8(cdata->_primary_file_num_channels);
   me.add_uint8(cdata->_alpha_file_channel);
   me.add_bool(has_rawdata);
-  me.add_uint8(cdata->_texture_type);
-  me.add_bool(cdata->_has_read_mipmaps);
+
+  if (manager->get_file_minor_ver() < 25 &&
+      cdata->_texture_type == TT_cube_map) {
+    // Between Panda3D releases 1.7.2 and 1.8.0 (bam versions 6.24 and 6.25),
+    // we added TT_2d_texture_array, shifting the definition for TT_cube_map.
+    me.add_uint8(TT_2d_texture_array);
+  } else {
+    me.add_uint8(cdata->_texture_type);
+  }
+
+  if (manager->get_file_minor_ver() >= 32) {
+    me.add_bool(cdata->_has_read_mipmaps);
+  }
 }
 
 /**
@@ -8673,7 +8694,18 @@ do_write_datagram_header(CData *cdata, BamWriter *manager, Datagram &me, bool &h
  */
 void Texture::
 do_write_datagram_body(CData *cdata, BamWriter *manager, Datagram &me) {
-  cdata->_default_sampler.write_datagram(me);
+  if (manager->get_file_minor_ver() >= 36) {
+    cdata->_default_sampler.write_datagram(me);
+  } else {
+    const SamplerState &s = cdata->_default_sampler;
+    me.add_uint8(s.get_wrap_u());
+    me.add_uint8(s.get_wrap_v());
+    me.add_uint8(s.get_wrap_w());
+    me.add_uint8(s.get_minfilter());
+    me.add_uint8(s.get_magfilter());
+    me.add_int16(s.get_anisotropic_degree());
+    s.get_border_color().write_datagram(me);
+  }
 
   me.add_uint8(cdata->_compression);
   me.add_uint8(cdata->_quality_level);
@@ -8685,7 +8717,9 @@ do_write_datagram_body(CData *cdata, BamWriter *manager, Datagram &me) {
     me.add_uint8(cdata->_usage_hint);
   }
 
-  me.add_uint8(cdata->_auto_texture_scale);
+  if (manager->get_file_minor_ver() >= 28) {
+    me.add_uint8(cdata->_auto_texture_scale);
+  }
   me.add_uint32(cdata->_orig_file_x_size);
   me.add_uint32(cdata->_orig_file_y_size);
 
@@ -8711,11 +8745,15 @@ do_write_datagram_rawdata(CData *cdata, BamWriter *manager, Datagram &me) {
   me.add_uint32(cdata->_y_size);
   me.add_uint32(cdata->_z_size);
 
-  me.add_uint32(cdata->_pad_x_size);
-  me.add_uint32(cdata->_pad_y_size);
-  me.add_uint32(cdata->_pad_z_size);
+  if (manager->get_file_minor_ver() >= 30) {
+    me.add_uint32(cdata->_pad_x_size);
+    me.add_uint32(cdata->_pad_y_size);
+    me.add_uint32(cdata->_pad_z_size);
+  }
 
-  me.add_uint32(cdata->_num_views);
+  if (manager->get_file_minor_ver() >= 26) {
+    me.add_uint32(cdata->_num_views);
+  }
   me.add_uint8(cdata->_component_type);
   me.add_uint8(cdata->_component_width);
   me.add_uint8(cdata->_ram_image_compression);

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

@@ -159,6 +159,7 @@ PUBLISHED:
     F_rgb10_a2,
 
     F_rg,
+    F_r16i
   };
 
   // Deprecated.  See SamplerState.FilterType.

+ 4 - 1
panda/src/gobj/textureStage.cxx

@@ -438,7 +438,10 @@ write_datagram(BamWriter *manager, Datagram &me) {
     me.add_uint8(_rgb_scale);
     me.add_uint8(_alpha_scale);
     me.add_bool(_saved_result);
-    me.add_int32(_tex_view_offset);
+
+    if (manager->get_file_minor_ver() >= 26) {
+      me.add_int32(_tex_view_offset);
+    }
 
     me.add_uint8(_combine_rgb_mode);
     me.add_uint8(_num_combine_rgb_operands);

+ 4 - 1
panda/src/grutil/movieTexture.cxx

@@ -816,7 +816,10 @@ do_write_datagram_rawdata(Texture::CData *cdata_tex, BamWriter *manager, Datagra
   CDReader cdata(_cycler);
 
   dg.add_uint16(cdata_tex->_z_size);
-  dg.add_uint16(cdata_tex->_num_views);
+  if (manager->get_file_minor_ver() >= 26) {
+    dg.add_uint16(cdata_tex->_num_views);
+  }
+
   nassertv(cdata->_pages.size() == (size_t)(cdata_tex->_z_size * cdata_tex->_num_views));
   for (size_t n = 0; n < cdata->_pages.size(); ++n) {
     const VideoPage &page = cdata->_pages[n];

+ 11 - 9
panda/src/grutil/shaderTerrainMesh.I

@@ -12,26 +12,28 @@
  */
 
 /**
- * @brief Sets the path to the heightfield
- * @details This sets the path to the terrain heightfield. It should be 16bit
+ * @brief Sets the heightfield texture
+ * @details This sets the heightfield texture. It should be 16bit
  *   single channel, and have a power-of-two resolution greater than 32.
  *   Common sizes are 2048x2048 or 4096x4096.
  *
- * @param filename Path to the heightfield
+ *   You should call generate() after setting the heightfield.
+ *
+ * @param filename Heightfield texture
  */
-INLINE void ShaderTerrainMesh::set_heightfield_filename(const Filename& filename) {
-  _heightfield_source = filename;
+INLINE void ShaderTerrainMesh::set_heightfield(Texture* heightfield) {
+  _heightfield_tex = heightfield;
 }
 
 /**
- * @brief Returns the heightfield path
- * @details This returns the path of the terrain heightfield, previously set with
+ * @brief Returns the heightfield
+ * @details This returns the terrain heightfield, previously set with
  *   set_heightfield()
  *
  * @return Path to the heightfield
  */
-INLINE const Filename& ShaderTerrainMesh::get_heightfield_filename() const {
-  return _heightfield_source;
+INLINE Texture* ShaderTerrainMesh::get_heightfield() const {
+  return _heightfield_tex;
 }
 
 /**

+ 28 - 34
panda/src/grutil/shaderTerrainMesh.cxx

@@ -35,7 +35,7 @@
 #include "typeHandle.h"
 
 ConfigVariableBool stm_use_hexagonal_layout
-("stm-use-hexagonal-layout", true,
+("stm-use-hexagonal-layout", false,
  PRC_DESC("Set this to true to use a hexagonal vertex layout. This approximates "
           "the heightfield in a better way, however the CLOD transitions might be "
           "visible due to the vertices not matching exactly."));
@@ -81,7 +81,6 @@ ShaderTerrainMesh::ShaderTerrainMesh() :
   PandaNode("ShaderTerrainMesh"),
   _size(0),
   _chunk_size(32),
-  _heightfield_source(""),
   _generate_patches(false),
   _data_texture(NULL),
   _chunk_geom(NULL),
@@ -107,7 +106,7 @@ ShaderTerrainMesh::ShaderTerrainMesh() :
  * @return true if the terrain was initialized, false if an error occured
  */
 bool ShaderTerrainMesh::generate() {
-  if (!do_load_heightfield())
+  if (!do_check_heightfield())
     return false;
 
   if (_chunk_size < 8 || !check_power_of_two(_chunk_size)) {
@@ -120,28 +119,29 @@ bool ShaderTerrainMesh::generate() {
     return false;
   }
 
+  do_extract_heightfield();
   do_create_chunks();
   do_compute_bounds(&_base_chunk);
   do_create_chunk_geom();
   do_init_data_texture();
-  do_convert_heightfield();
+
+  // Clear image after using it, otherwise we have two copies of the heightfield
+  // in memory.
+  _heightfield.clear();
 
   return true;
 }
 
 /**
- * @brief Converts the internal used PNMImage to a Texture
- * @details This converts the internal used PNMImage to a texture object. The
- *   reason for this is, that we need the PNMimage for computing the chunk
- *   bounds, but don't need it afterwards. However, since we have it in ram,
- *   we can just put its contents into a Texture object, which enables the
- *   user to call get_heightfield() instead of manually loading the texture
- *   from disk again to set it as shader input (Panda does not cache PNMImages)
+ * @brief Converts the internal used Texture to a PNMImage
+ * @details This converts the texture passed with set_heightfield to a PNMImage,
+ *   so we can read the pixels in a fast way. This is only used while generating
+ *   the chunks, and the PNMImage is destroyed afterwards.
  */
-void ShaderTerrainMesh::do_convert_heightfield() {
-  _heightfield_tex = new Texture();
-  _heightfield_tex->load(_heightfield);
-  _heightfield_tex->set_keep_ram_image(true);
+void ShaderTerrainMesh::do_extract_heightfield() {
+  nassertv(_heightfield_tex->has_ram_image()); // Heightfield not in RAM, extract ram image first
+
+  _heightfield_tex->store(_heightfield);
 
   if (_heightfield.get_maxval() != 65535) {
     shader_terrain_cat.warning() << "Using non 16-bit heightfield!" << endl;
@@ -150,31 +150,23 @@ void ShaderTerrainMesh::do_convert_heightfield() {
   }
   _heightfield_tex->set_minfilter(SamplerState::FT_linear);
   _heightfield_tex->set_magfilter(SamplerState::FT_linear);
-  _heightfield.clear();
 }
 
 /**
- * @brief Intermal method to load the heightfield
- * @details This method loads the heightfield from the heightfield path,
+ * @brief Intermal method to check the heightfield
+ * @details This method cecks the heightfield generated from the heightfield texture,
  *   and performs some basic checks, including a check for a power of two,
  *   and same width and height.
  *
- * @return true if the heightfield was loaded and meets the requirements
+ * @return true if the heightfield meets the requirements
  */
-bool ShaderTerrainMesh::do_load_heightfield() {
-
-  if(!_heightfield.read(_heightfield_source)) {
-    shader_terrain_cat.error() << "Could not load heightfield from " << _heightfield_source << endl;
-    return false;
-  }
-
-  if (_heightfield.get_x_size() != _heightfield.get_y_size()) {
+bool ShaderTerrainMesh::do_check_heightfield() {
+  if (_heightfield_tex->get_x_size() != _heightfield_tex->get_y_size()) {
     shader_terrain_cat.error() << "Only square heightfields are supported!";
     return false;
   }
 
-  _size = _heightfield.get_x_size();
-
+  _size = _heightfield_tex->get_x_size();
   if (_size < 32 || !check_power_of_two(_size)) {
     shader_terrain_cat.error() << "Invalid heightfield! Needs to be >= 32 and a power of two (was: "
          << _size << ")!" << endl;
@@ -383,8 +375,8 @@ void ShaderTerrainMesh::do_create_chunk_geom() {
       // Stitched vertices at the cornders
       if (x == -1 || y == -1 || x == size + 1 || y == size + 1) {
         vtx_pos.set_z(-1.0f / (PN_stdfloat)size);
-        vtx_pos.set_x(max(0.0f, min(1.0f, vtx_pos.get_x())));
-        vtx_pos.set_y(max(0.0f, min(1.0f, vtx_pos.get_y())));
+        vtx_pos.set_x(max((PN_stdfloat)0, min((PN_stdfloat)1, vtx_pos.get_x())));
+        vtx_pos.set_y(max((PN_stdfloat)0, min((PN_stdfloat)1, vtx_pos.get_y())));
       }
       vertex_writer.add_data3(vtx_pos);
     }
@@ -687,8 +679,8 @@ void ShaderTerrainMesh::do_emit_chunk(Chunk* chunk, TraversalData* data) {
   data_entry.size = chunk->size / _chunk_size;
   data_entry.clod = chunk->last_clod;
 
-  data->emitted_chunks ++;
-  data->storage_ptr ++;
+  data->emitted_chunks++;
+  data->storage_ptr++;
 }
 
 /**
@@ -701,7 +693,9 @@ void ShaderTerrainMesh::do_emit_chunk(Chunk* chunk, TraversalData* data) {
  * @return World-Space point
  */
 LPoint3 ShaderTerrainMesh::uv_to_world(const LTexCoord& coord) const {
-  nassertr(_heightfield_tex != NULL, LPoint3(0));
+  nassertr(_heightfield_tex != NULL, LPoint3(0)); // Heightfield not set yet
+  nassertr(_heightfield_tex->has_ram_image(), LPoint3(0)); // Heightfield not in memory
+
   PT(TexturePeeker) peeker = _heightfield_tex->peek();
   nassertr(peeker != NULL, LPoint3(0));
 

+ 5 - 6
panda/src/grutil/shaderTerrainMesh.h

@@ -55,9 +55,9 @@ PUBLISHED:
 
   ShaderTerrainMesh();
 
-  INLINE void set_heightfield_filename(const Filename& filename);
-  INLINE const Filename& get_heightfield_filename() const;
-  MAKE_PROPERTY(heightfield_filename, get_heightfield_filename, set_heightfield_filename);
+  INLINE void set_heightfield(Texture* heightfield);
+  INLINE Texture* get_heightfield() const;
+  MAKE_PROPERTY(heightfield, get_heightfield, set_heightfield);
 
   INLINE void set_chunk_size(size_t chunk_size);
   INLINE size_t get_chunk_size() const;
@@ -152,8 +152,8 @@ private:
     ChunkDataEntry* storage_ptr;
   };
 
-  bool do_load_heightfield();
-  void do_convert_heightfield();
+  bool do_check_heightfield();
+  void do_extract_heightfield();
   void do_init_data_texture();
   void do_create_chunks();
   void do_init_chunk(Chunk* chunk);
@@ -164,7 +164,6 @@ private:
   bool do_check_lod_matches(Chunk* chunk, TraversalData* data);
 
   Chunk _base_chunk;
-  Filename _heightfield_source;
   size_t _size;
   size_t _chunk_size;
   bool _generate_patches;

+ 3 - 3
panda/src/movies/dr_flac.h

@@ -92,7 +92,7 @@
 
 #include <stddef.h>
 #include <stdint.h>
-#include <stdbool.h>
+//#include <stdbool.h>
 
 // As data is read from the client it is placed into an internal buffer for fast access. This controls the
 // size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing
@@ -2244,7 +2244,7 @@ done_reading_block_header:
     }
 
     if (pIsLastBlockOut) {
-        *pIsLastBlockOut = isLastBlock;
+        *pIsLastBlockOut = (isLastBlock != 0);
     }
 
     return blockType;
@@ -2350,7 +2350,7 @@ static bool drflac__seek_to_sample__brute_force(drflac* pFlac, uint64_t sampleIn
         return false;
     }
 
-    return drflac_read_s16(pFlac, samplesToDecode, NULL);
+    return (drflac_read_s16(pFlac, samplesToDecode, NULL) != 0);
 }
 
 static bool drflac__seek_to_sample__seek_table(drflac* pFlac, uint64_t sampleIndex)

+ 18 - 4
panda/src/pgraph/clipPlaneAttrib.cxx

@@ -872,18 +872,32 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 
   // write the number of off_planes
   dg.add_uint16(get_num_off_planes());
+
   // write the off planes pointers if any
   Planes::const_iterator fi;
-  for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
-    (*fi).write_datagram(manager, dg);
+  if (manager->get_file_minor_ver() < 40) {
+    for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
+      manager->write_pointer(dg, fi->node());
+    }
+  } else {
+    for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
+      (*fi).write_datagram(manager, dg);
+    }
   }
 
   // write the number of on planes
   dg.add_uint16(get_num_on_planes());
+
   // write the on planes pointers if any
   Planes::const_iterator nti;
-  for (nti = _on_planes.begin(); nti != _on_planes.end(); ++nti) {
-    (*nti).write_datagram(manager, dg);
+  if (manager->get_file_minor_ver() < 40) {
+    for (nti = _on_planes.begin(); nti != _on_planes.end(); ++nti) {
+      manager->write_pointer(dg, nti->node());
+    }
+  } else {
+    for (nti = _on_planes.begin(); nti != _on_planes.end(); ++nti) {
+      (*nti).write_datagram(manager, dg);
+    }
   }
 }
 

+ 4 - 2
panda/src/pgraph/depthOffsetAttrib.cxx

@@ -162,8 +162,10 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
 
   dg.add_int32(_offset);
-  dg.add_stdfloat(_min_value);
-  dg.add_stdfloat(_max_value);
+  if (manager->get_file_minor_ver() >= 31) {
+    dg.add_stdfloat(_min_value);
+    dg.add_stdfloat(_max_value);
+  }
 }
 
 /**

+ 7 - 3
panda/src/pgraph/light.cxx

@@ -206,9 +206,13 @@ fill_viz_geom(GeomNode *) {
  */
 void Light::
 write_datagram(BamWriter *manager, Datagram &dg) {
-  dg.add_bool(_has_color_temperature);
-  if (_has_color_temperature) {
-    dg.add_stdfloat(_color_temperature);
+  if (manager->get_file_minor_ver() >= 39) {
+    dg.add_bool(_has_color_temperature);
+    if (_has_color_temperature) {
+      dg.add_stdfloat(_color_temperature);
+    } else {
+      manager->write_cdata(dg, _cycler);
+    }
   } else {
     manager->write_cdata(dg, _cycler);
   }

+ 17 - 4
panda/src/pgraph/lightAttrib.cxx

@@ -898,18 +898,31 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 
   // write the number of off_lights
   dg.add_uint16(get_num_off_lights());
+
   // write the off lights pointers if any
   Lights::const_iterator fi;
-  for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
-    (*fi).write_datagram(manager, dg);
+  if (manager->get_file_minor_ver() < 40) {
+    for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
+      manager->write_pointer(dg, fi->node());
+    }
+  } else {
+    for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
+      (*fi).write_datagram(manager, dg);
+    }
   }
 
   // write the number of on lights
   dg.add_uint16(get_num_on_lights());
   // write the on lights pointers if any
   Lights::const_iterator nti;
-  for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
-    (*nti).write_datagram(manager, dg);
+  if (manager->get_file_minor_ver() < 40) {
+    for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
+      manager->write_pointer(dg, nti->node());
+    }
+  } else {
+    for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
+      (*nti).write_datagram(manager, dg);
+    }
   }
 }
 

+ 8 - 8
panda/src/pgraph/loader.cxx

@@ -140,10 +140,10 @@ load_file(const Filename &filename, const LoaderOptions &options) const {
     extension = this_filename.get_extension();
   }
 
-  bool pz_file = false;
+  bool compressed = false;
 #ifdef HAVE_ZLIB
-  if (extension == "pz") {
-    pz_file = true;
+  if (extension == "pz" || extension == "gz") {
+    compressed = true;
     extension = Filename(this_filename.get_basename_wo_extension()).get_extension();
   }
 #endif  // HAVE_ZLIB
@@ -182,7 +182,7 @@ load_file(const Filename &filename, const LoaderOptions &options) const {
         << extension << ") does not support loading.\n";
     }
     return NULL;
-  } else if (pz_file && !requested_type->supports_compressed()) {
+  } else if (compressed && !requested_type->supports_compressed()) {
     if (report_errors) {
       loader_cat.error()
         << requested_type->get_name() << " file type (."
@@ -382,10 +382,10 @@ save_file(const Filename &filename, const LoaderOptions &options,
     extension = this_filename.get_extension();
   }
 
-  bool pz_file = false;
+  bool compressed = false;
 #ifdef HAVE_ZLIB
-  if (extension == "pz") {
-    pz_file = true;
+  if (extension == "pz" || extension == "gz") {
+    compressed = true;
     extension = Filename(this_filename.get_basename_wo_extension()).get_extension();
   }
 #endif  // HAVE_ZLIB
@@ -422,7 +422,7 @@ save_file(const Filename &filename, const LoaderOptions &options,
     }
     return false;
 
-  } else if (pz_file && !requested_type->supports_compressed()) {
+  } else if (compressed && !requested_type->supports_compressed()) {
     if (report_errors) {
       loader_cat.error()
         << requested_type->get_name() << " file type (."

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

@@ -48,7 +48,7 @@ get_additional_extensions() const {
 
 /**
  * Returns true if this file type can transparently load compressed files
- * (with a .pz extension), false otherwise.
+ * (with a .pz or .gz extension), false otherwise.
  */
 bool LoaderFileType::
 supports_compressed() const {

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

@@ -47,7 +47,7 @@ get_extension() const {
 
 /**
  * Returns true if this file type can transparently load compressed files
- * (with a .pz extension), false otherwise.
+ * (with a .pz or .gz extension), false otherwise.
  */
 bool LoaderFileTypeBam::
 supports_compressed() const {

+ 9 - 2
panda/src/pgraph/occluderEffect.cxx

@@ -146,10 +146,17 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 
   // write the number of on occluders
   dg.add_uint16(get_num_on_occluders());
+
   // write the on occluders pointers if any
   Occluders::const_iterator nti;
-  for (nti = _on_occluders.begin(); nti != _on_occluders.end(); ++nti) {
-    (*nti).write_datagram(manager, dg);
+  if (manager->get_file_minor_ver() < 40) {
+    for (nti = _on_occluders.begin(); nti != _on_occluders.end(); ++nti) {
+      manager->write_pointer(dg, nti->node());
+    }
+  } else {
+    for (nti = _on_occluders.begin(); nti != _on_occluders.end(); ++nti) {
+      (*nti).write_datagram(manager, dg);
+    }
   }
 }
 

+ 12 - 1
panda/src/pgraph/paramNodePath.cxx

@@ -40,7 +40,18 @@ register_with_read_factory() {
 void ParamNodePath::
 write_datagram(BamWriter *manager, Datagram &dg) {
   ParamValueBase::write_datagram(manager, dg);
-  _node_path.write_datagram(manager, dg);
+
+  if (manager->get_file_minor_ver() < 40) {
+    // Before bam 6.40, we did not support writing NodePaths.  Instaed, we
+    // write the PandaNode pointer and pray there is an unambiguous path.
+    if (_node_path.is_empty()) {
+      manager->write_pointer(dg, NULL);
+    } else {
+      manager->write_pointer(dg, _node_path.node());
+    }
+  } else {
+    _node_path.write_datagram(manager, dg);
+  }
 }
 
 /**

+ 3 - 1
panda/src/pgraph/scissorAttrib.cxx

@@ -177,7 +177,9 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
 
   _frame.write_datagram(dg);
-  dg.add_bool(_off);
+  if (manager->get_file_minor_ver() >= 34) {
+    dg.add_bool(_off);
+  }
 }
 
 /**

+ 20 - 2
panda/src/pgraph/stencilAttrib.cxx

@@ -338,8 +338,26 @@ void StencilAttrib::
 write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, dg);
 
-  for (int index = 0; index < SRS_total; ++index) {
-    dg.add_uint32(_stencil_render_states[index]);
+  if (manager->get_file_minor_ver() < 35) {
+    dg.add_int32(_stencil_render_states[SRS_front_comparison_function] != M_none);
+    dg.add_int32(_stencil_render_states[SRS_back_comparison_function] != M_none);
+
+    for (int index = 0; index < SRS_total; ++index) {
+      if (index == SRS_front_comparison_function ||
+          index == SRS_back_comparison_function) {
+        if (_stencil_render_states[index] == M_none) {
+          dg.add_uint32(7);
+        } else {
+          dg.add_uint32(_stencil_render_states[index] - 1);
+        }
+      } else {
+        dg.add_uint32(_stencil_render_states[index]);
+      }
+    }
+  } else {
+    for (int index = 0; index < SRS_total; ++index) {
+      dg.add_uint32(_stencil_render_states[index]);
+    }
   }
 }
 

+ 4 - 1
panda/src/pgraph/texMatrixAttrib.cxx

@@ -461,7 +461,10 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 
     manager->write_pointer(dg, sn._stage);
     manager->write_pointer(dg, sn._transform);
-    dg.add_int32(sn._override);
+
+    if (manager->get_file_minor_ver() >= 24) {
+      dg.add_int32(sn._override);
+    }
   }
 }
 

+ 10 - 4
panda/src/pgraph/textureAttrib.cxx

@@ -778,10 +778,16 @@ write_datagram(BamWriter *manager, Datagram &dg) {
     manager->write_pointer(dg, stage);
     manager->write_pointer(dg, tex);
     dg.add_uint16((*si)._implicit_sort);
-    dg.add_int32((*si)._override);
-    dg.add_bool((*si)._has_sampler);
-    if ((*si)._has_sampler) {
-      (*si)._sampler.write_datagram(dg);
+
+    if (manager->get_file_minor_ver() >= 23) {
+      dg.add_int32((*si)._override);
+    }
+
+    if (manager->get_file_minor_ver() >= 36) {
+      dg.add_bool((*si)._has_sampler);
+      if ((*si)._has_sampler) {
+        (*si)._sampler.write_datagram(dg);
+      }
     }
   }
 }

+ 3 - 1
panda/src/pgraphnodes/directionalLight.cxx

@@ -167,7 +167,9 @@ register_with_read_factory() {
 void DirectionalLight::
 write_datagram(BamWriter *manager, Datagram &dg) {
   LightLensNode::write_datagram(manager, dg);
-  dg.add_bool(_has_specular_color);
+  if (manager->get_file_minor_ver() >= 39) {
+    dg.add_bool(_has_specular_color);
+  }
   manager->write_cdata(dg, _cycler);
 }
 

+ 3 - 1
panda/src/pgraphnodes/pointLight.cxx

@@ -196,7 +196,9 @@ register_with_read_factory() {
 void PointLight::
 write_datagram(BamWriter *manager, Datagram &dg) {
   LightLensNode::write_datagram(manager, dg);
-  dg.add_bool(_has_specular_color);
+  if (manager->get_file_minor_ver() >= 39) {
+    dg.add_bool(_has_specular_color);
+  }
   manager->write_cdata(dg, _cycler);
 }
 

+ 3 - 1
panda/src/pgraphnodes/spotlight.cxx

@@ -252,7 +252,9 @@ register_with_read_factory() {
 void Spotlight::
 write_datagram(BamWriter *manager, Datagram &dg) {
   LightLensNode::write_datagram(manager, dg);
-  dg.add_bool(_has_specular_color);
+  if (manager->get_file_minor_ver() >= 39) {
+    dg.add_bool(_has_specular_color);
+  }
   manager->write_cdata(dg, _cycler);
 }
 

+ 6 - 2
panda/src/pgraphnodes/uvScrollNode.cxx

@@ -53,8 +53,12 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   PandaNode::write_datagram(manager, dg);
   dg.add_stdfloat(_u_speed);
   dg.add_stdfloat(_v_speed);
-  dg.add_stdfloat(_w_speed);
-  dg.add_stdfloat(_r_speed);
+  if (manager->get_file_minor_ver() >= 33) {
+    dg.add_stdfloat(_w_speed);
+  }
+  if (manager->get_file_minor_ver() >= 22) {
+    dg.add_stdfloat(_r_speed);
+  }
 }
 
 /**

+ 1 - 1
panda/src/pnmimage/pnmFileTypeRegistry.cxx

@@ -127,7 +127,7 @@ get_type_from_extension(const string &filename) const {
   }
 
 #ifdef HAVE_ZLIB
-  if (extension == "pz") {
+  if (extension == "pz" || extension == "gz") {
     // If the extension is .pz, then we've got a Panda-compressed image file.
     // Back up some more and get the extension before that.
     size_t prev_dot = filename.rfind('.', dot - 1);

+ 3 - 2
panda/src/pnmimage/pnmImageHeader.cxx

@@ -181,7 +181,8 @@ make_reader(istream *file, bool owns_file, const Filename &filename,
           << type->get_name() << ".\n";
       } else {
         pnmimage_cat.debug()
-          << "Unable to guess image file type from its extension.\n";
+          << "Unable to guess image file type from its extension ("
+          << filename.get_extension() << ").\n";
       }
     }
   }
@@ -342,7 +343,7 @@ make_writer(ostream *file, bool owns_file, const Filename &filename,
     delete file;
   }
 
-  if (!writer->is_valid()) {
+  if (writer != NULL && !writer->is_valid()) {
     delete writer;
     writer = NULL;
   }

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

@@ -42,6 +42,16 @@ get_file_major_ver() const {
   return _file_major;
 }
 
+/**
+ * Changes the minor .bam version to write.  This should be called before
+ * init().  Each Panda version has only a fairly narrow range of versions it
+ * is able to write; consult the .bam documentation for more information.
+ */
+INLINE void BamWriter::
+set_file_minor_ver(int minor_ver) {
+  _file_minor = minor_ver;
+}
+
 /**
  * Returns the minor version number of the Bam file currently being written.
  */

+ 48 - 7
panda/src/putil/bamWriter.cxx

@@ -41,8 +41,43 @@ BamWriter(DatagramSink *target) :
   _next_pta_id = 1;
   _long_pta_id = false;
 
-  _file_major = _bam_major_ver;
-  _file_minor = _bam_minor_ver;
+  // Check which version .bam files we should write.
+  if (bam_version.get_num_words() > 0) {
+    if (bam_version.get_num_words() != 2) {
+      util_cat.error()
+        << "bam-version configuration variable requires two arguments.\n";
+    }
+    _file_major = bam_version[0];
+    _file_minor = bam_version[1];
+
+    if (_file_major < _bam_major_ver || _file_minor < 21) {
+      util_cat.error()
+        << "bam-version is set to " << bam_version << ", but this version of "
+           "Panda3D cannot produce .bam files older than 6.21.  Set "
+           "bam-version to 6 21 in Config.prc to suppress this error, or "
+           "leave it blank to write version " << _bam_major_ver << "."
+           << _bam_minor_ver << " files.\n";
+      _file_major = 6;
+      _file_minor = 21;
+      bam_version.set_string_value("6 21");
+
+    } else if (_file_major > _bam_major_ver || _file_minor > _bam_minor_ver) {
+      util_cat.error()
+        << "bam-version is set to " << bam_version << ", but this version of "
+           "Panda3D cannot produce .bam files newer than " << _bam_major_ver
+        << "." << _bam_minor_ver << ".  Set bam-version to a supported "
+           "version or leave it blank to write version " << _bam_major_ver
+        << "." << _bam_minor_ver << " files.\n";
+
+      _file_major = _bam_major_ver;
+      _file_minor = _bam_minor_ver;
+      bam_version.set_word(0, _bam_major_ver);
+      bam_version.set_word(1, _bam_minor_ver);
+    }
+  } else {
+    _file_major = _bam_major_ver;
+    _file_minor = _bam_minor_ver;
+  }
   _file_endian = bam_endian;
   _file_stdfloat_double = bam_stdfloat_double;
   _file_texture_mode = bam_texture_mode;
@@ -98,18 +133,24 @@ init() {
   _next_pta_id = 1;
   _long_pta_id = false;
 
-  _file_major = _bam_major_ver;
-  _file_minor = _bam_minor_ver;
+  nassertr_always(_file_major == _bam_major_ver, false);
+  nassertr_always(_file_minor <= _bam_minor_ver && _file_minor >= 21, false);
+
   _file_endian = bam_endian;
   _file_texture_mode = bam_texture_mode;
 
   // Write out the current major and minor BAM file version numbers.
   Datagram header;
 
-  header.add_uint16(_bam_major_ver);
-  header.add_uint16(_bam_minor_ver);
+  header.add_uint16(_file_major);
+  header.add_uint16(_file_minor);
   header.add_uint8(_file_endian);
-  header.add_bool(_file_stdfloat_double);
+
+  if (_file_major >= 6 || _file_minor >= 27) {
+    header.add_bool(_file_stdfloat_double);
+  } else {
+    _file_stdfloat_double = false;
+  }
 
   if (!_target->put_datagram(header)) {
     util_cat.error()

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

@@ -76,6 +76,7 @@ PUBLISHED:
 
   INLINE int get_file_major_ver() const;
   INLINE int get_file_minor_ver() const;
+  INLINE void set_file_minor_ver(int minor_ver);
 
   INLINE BamEndian get_file_endian() const;
   INLINE bool get_file_stdfloat_double() const;

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

@@ -51,6 +51,12 @@ ConfigureDef(config_util);
 NotifyCategoryDef(util, "");
 NotifyCategoryDef(bam, util_cat);
 
+ConfigVariableInt bam_version
+("bam-version", "",
+ PRC_DESC("Set this to specify which version .bam files to generate.  Each "
+          "Panda version only supports outputting a limited number of .bam "
+          "versions.  The default is to use the latest supported version."));
+
 ConfigVariableEnum<BamEnums::BamEndian> bam_endian
 ("bam-endian", BamEnums::BE_native,
  PRC_DESC("The default endianness to use for writing major numeric data "

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

@@ -33,6 +33,7 @@ NotifyCategoryDecl(bam, EXPCL_PANDA_PUTIL, EXPTP_PANDA_PUTIL);
 // a global constant, we'll make it a member of MemoryUsage.  extern
 // EXPCL_PANDA_PUTIL const bool track_memory_usage;
 
+extern EXPCL_PANDA_PUTIL ConfigVariableInt bam_version;
 extern EXPCL_PANDA_PUTIL ConfigVariableEnum<BamEnums::BamEndian> bam_endian;
 extern EXPCL_PANDA_PUTIL ConfigVariableBool bam_stdfloat_double;
 extern EXPCL_PANDA_PUTIL ConfigVariableEnum<BamEnums::BamTextureMode> bam_texture_mode;

+ 1 - 1
panda/src/speedtree/loaderFileTypeSrt.cxx

@@ -42,7 +42,7 @@ get_extension() const {
 
 /**
  * Returns true if this file type can transparently load compressed files
- * (with a .pz extension), false otherwise.
+ * (with a .pz or .gz extension), false otherwise.
  */
 bool LoaderFileTypeSrt::
 supports_compressed() const {

+ 1 - 1
panda/src/speedtree/loaderFileTypeStf.cxx

@@ -41,7 +41,7 @@ get_extension() const {
 
 /**
  * Returns true if this file type can transparently load compressed files
- * (with a .pz extension), false otherwise.
+ * (with a .pz or .gz extension), false otherwise.
  */
 bool LoaderFileTypeStf::
 supports_compressed() const {

+ 1 - 1
pandatool/src/assimp/loaderFileTypeAssimp.cxx

@@ -63,7 +63,7 @@ get_additional_extensions() const {
 
 /**
  * Returns true if this file type can transparently load compressed files
- * (with a .pz extension), false otherwise.
+ * (with a .pz or .gz extension), false otherwise.
  */
 bool LoaderFileTypeAssimp::
 supports_compressed() const {

+ 1 - 1
pandatool/src/ptloader/loaderFileTypePandatool.cxx

@@ -80,7 +80,7 @@ get_additional_extensions() const {
 
 /**
  * Returns true if this file type can transparently load compressed files
- * (with a .pz extension), false otherwise.
+ * (with a .pz or .gz extension), false otherwise.
  */
 bool LoaderFileTypePandatool::
 supports_compressed() const {

+ 13 - 11
samples/shader-terrain/main.py

@@ -2,16 +2,15 @@
 
 # Author: tobspr
 #
-# Last Updated: 2016-02-13
+# Last Updated: 2016-04-30
 #
 # This tutorial provides an example of using the ShaderTerrainMesh class
 
-import os, sys, math, random
-
 from direct.showbase.ShowBase import ShowBase
 from panda3d.core import ShaderTerrainMesh, Shader, load_prc_file_data
 from panda3d.core import SamplerState
 
+
 class ShaderTerrainDemo(ShowBase):
     def __init__(self):
 
@@ -19,14 +18,14 @@ class ShaderTerrainDemo(ShowBase):
         # before the ShowBase is initialized
         load_prc_file_data("", """
             textures-power-2 none
-            window-title Panda3D Shader Terrain Demo
             gl-coordinate-system default
+            window-title Panda3D ShaderTerrainMesh Demo
         """)
 
         # Initialize the showbase
         ShowBase.__init__(self)
 
-        # Increase camera FOV aswell as the far plane
+        # Increase camera FOV as well as the far plane
         self.camLens.set_fov(90)
         self.camLens.set_near_far(0.1, 50000)
 
@@ -35,7 +34,7 @@ class ShaderTerrainDemo(ShowBase):
 
         # Set a heightfield, the heightfield should be a 16-bit png and
         # have a quadratic size of a power of two.
-        self.terrain_node.heightfield_filename = "heightfield.png"
+        self.terrain_node.heightfield = self.loader.loadTexture("heightfield.png")
 
         # Set the target triangle width. For a value of 10.0 for example,
         # the terrain will attempt to make every triangle 10 pixels wide on screen.
@@ -44,24 +43,28 @@ class ShaderTerrainDemo(ShowBase):
         # Generate the terrain
         self.terrain_node.generate()
 
-        # Attach the terrain to the main scene and set its scale
+        # Attach the terrain to the main scene and set its scale. With no scale
+        # set, the terrain ranges from (0, 0, 0) to (1, 1, 1)
         self.terrain = self.render.attach_new_node(self.terrain_node)
         self.terrain.set_scale(1024, 1024, 100)
         self.terrain.set_pos(-512, -512, -70.0)
 
         # Set a shader on the terrain. The ShaderTerrainMesh only works with
-        # an applied shader. You can use the shaders used here in your own shaders
+        # an applied shader. You can use the shaders used here in your own application
         terrain_shader = Shader.load(Shader.SL_GLSL, "terrain.vert.glsl", "terrain.frag.glsl")
         self.terrain.set_shader(terrain_shader)
         self.terrain.set_shader_input("camera", self.camera)
 
+        # Shortcut to view the wireframe mesh
+        self.accept("f3", self.toggleWireframe)
+
         # Set some texture on the terrain
         grass_tex = self.loader.loadTexture("textures/grass.png")
         grass_tex.set_minfilter(SamplerState.FT_linear_mipmap_linear)
         grass_tex.set_anisotropic_degree(16)
         self.terrain.set_texture(grass_tex)
 
-        # Load some skybox - you can safely ignore this code
+        # Load a skybox - you can safely ignore this code
         skybox = self.loader.loadModel("models/skybox.bam")
         skybox.reparent_to(self.render)
         skybox.set_scale(20000)
@@ -77,5 +80,4 @@ class ShaderTerrainDemo(ShowBase):
         skybox_shader = Shader.load(Shader.SL_GLSL, "skybox.vert.glsl", "skybox.frag.glsl")
         skybox.set_shader(skybox_shader)
 
-demo = ShaderTerrainDemo()
-demo.run()
+ShaderTerrainDemo().run()