Parcourir la source

Merge branch 'master' into vulkan

rdb il y a 9 ans
Parent
commit
75ccc1d6bf
100 fichiers modifiés avec 3341 ajouts et 1808 suppressions
  1. 1 1
      direct/src/fsm/FSM.py
  2. 24 25
      direct/src/gui/DirectScrolledList.py
  3. 1 2
      direct/src/gui/OnscreenText.py
  4. 16 16
      direct/src/interval/MetaInterval.py
  5. 1 0
      direct/src/showbase/ShowBaseGlobal.py
  6. 0 374
      direct/src/showbase/pandaSqueezeTool.py
  7. 0 57
      direct/src/showbase/pandaSqueezer.py
  8. 17 3
      dtool/src/dtoolbase/pvector.h
  9. 3 2
      dtool/src/dtoolbase/typeRegistry.h
  10. 14 0
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  11. 1 0
      dtool/src/parser-inc/sys/time.h
  12. 0 6
      makepanda/installer.nsi
  13. 22 12
      makepanda/makepanda.py
  14. 49 19
      makepanda/makepandacore.py
  15. 1 1
      panda/src/bullet/bulletContactResult.I
  16. 1 1
      panda/src/bullet/bulletContactResult.h
  17. 1 1
      panda/src/bullet/bulletHeightfieldShape.I
  18. 4 0
      panda/src/bullet/bulletTriangleMesh.cxx
  19. 10 4
      panda/src/chan/animChannelMatrixXfmTable.cxx
  20. 10 4
      panda/src/chan/animChannelScalarTable.cxx
  21. 11 0
      panda/src/chan/config_chan.cxx
  22. 9 9
      panda/src/display/drawableRegion.I
  23. 10 13
      panda/src/display/drawableRegion.cxx
  24. 1 1
      panda/src/display/drawableRegion.h
  25. 7 0
      panda/src/display/frameBufferProperties.cxx
  26. 3 0
      panda/src/display/graphicsEngine.cxx
  27. 4 1
      panda/src/display/graphicsStateGuardian.cxx
  28. 12 0
      panda/src/downloader/socketStream.h
  29. 5 0
      panda/src/dxgsg9/config_dxgsg9.cxx
  30. 6 7
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  31. 11 5
      panda/src/gles2gsg/gles2gsg.h
  32. 760 1050
      panda/src/gles2gsg/panda_esgl2ext.h
  33. 4 5
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  34. 91 43
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  35. 9 8
      panda/src/glstuff/glShaderContext_src.cxx
  36. 2 1
      panda/src/gobj/geomPrimitive.cxx
  37. 9 0
      panda/src/gobj/geomVertexFormat.I
  38. 2 4
      panda/src/gobj/geomVertexFormat.cxx
  39. 4 0
      panda/src/gobj/geomVertexFormat.h
  40. 5 3
      panda/src/gobj/shader.cxx
  41. 174 78
      panda/src/gobj/texture.cxx
  42. 2 0
      panda/src/grutil/config_grutil.cxx
  43. 1 0
      panda/src/grutil/p3grutil_composite1.cxx
  44. 191 0
      panda/src/grutil/shaderTerrainMesh.I
  45. 715 0
      panda/src/grutil/shaderTerrainMesh.cxx
  46. 205 0
      panda/src/grutil/shaderTerrainMesh.h
  47. 4 0
      panda/src/pgraph/alphaTestAttrib.h
  48. 5 0
      panda/src/pgraph/antialiasAttrib.h
  49. 3 0
      panda/src/pgraph/audioVolumeAttrib.h
  50. 3 0
      panda/src/pgraph/auxBitplaneAttrib.h
  51. 8 3
      panda/src/pgraph/camera.cxx
  52. 4 0
      panda/src/pgraph/colorAttrib.h
  53. 7 0
      panda/src/pgraph/colorBlendAttrib.cxx
  54. 11 0
      panda/src/pgraph/colorBlendAttrib.h
  55. 3 0
      panda/src/pgraph/colorScaleAttrib.h
  56. 3 0
      panda/src/pgraph/colorWriteAttrib.h
  57. 4 0
      panda/src/pgraph/cullBinAttrib.h
  58. 5 0
      panda/src/pgraph/cullFaceAttrib.h
  59. 5 0
      panda/src/pgraph/depthOffsetAttrib.h
  60. 3 0
      panda/src/pgraph/depthTestAttrib.h
  61. 3 0
      panda/src/pgraph/depthWriteAttrib.h
  62. 3 0
      panda/src/pgraph/fogAttrib.h
  63. 3 0
      panda/src/pgraph/lightRampAttrib.h
  64. 3 0
      panda/src/pgraph/materialAttrib.h
  65. 6 1
      panda/src/pgraph/renderModeAttrib.h
  66. 1 0
      panda/src/pgraph/rescaleNormalAttrib.h
  67. 3 0
      panda/src/pgraph/scissorAttrib.h
  68. 1 0
      panda/src/pgraph/shadeModelAttrib.h
  69. 4 0
      panda/src/pgraph/shaderAttrib.h
  70. 1 0
      panda/src/pgraph/transparencyAttrib.h
  71. 3 0
      panda/src/pgraphnodes/config_pgraphnodes.cxx
  72. 2 0
      panda/src/pgraphnodes/p3pgraphnodes_composite1.cxx
  73. 1 2
      panda/src/pgraphnodes/p3pgraphnodes_composite2.cxx
  74. 48 0
      panda/src/pgraphnodes/sphereLight.I
  75. 146 0
      panda/src/pgraphnodes/sphereLight.cxx
  76. 90 0
      panda/src/pgraphnodes/sphereLight.h
  77. 11 0
      panda/src/putil/bamReader.I
  78. 9 0
      panda/src/putil/bamReader.h
  79. 123 0
      panda/src/putil/bamReader_ext.cxx
  80. 39 0
      panda/src/putil/bamReader_ext.h
  81. 2 2
      panda/src/putil/factory.I
  82. 2 1
      panda/src/putil/factory.h
  83. 18 10
      panda/src/putil/factoryBase.cxx
  84. 8 12
      panda/src/putil/factoryBase.h
  85. 48 0
      panda/src/putil/factoryParams.I
  86. 0 14
      panda/src/putil/factoryParams.cxx
  87. 13 2
      panda/src/putil/factoryParams.h
  88. 3 0
      panda/src/putil/p3putil_ext_composite.cxx
  89. 3 0
      panda/src/putil/typedWritable.h
  90. 8 0
      panda/src/vrpn/vrpn_interface.h
  91. 7 5
      panda/src/wgldisplay/wglGraphicsBuffer.cxx
  92. BIN
      samples/shader-terrain/heightfield.png
  93. 81 0
      samples/shader-terrain/main.py
  94. BIN
      samples/shader-terrain/models/skybox.bam
  95. 21 0
      samples/shader-terrain/skybox.frag.glsl
  96. 13 0
      samples/shader-terrain/skybox.vert.glsl
  97. 56 0
      samples/shader-terrain/terrain.frag.glsl
  98. 56 0
      samples/shader-terrain/terrain.vert.glsl
  99. 5 0
      samples/shader-terrain/textures/LICENSE.txt
  100. BIN
      samples/shader-terrain/textures/grass.png

+ 1 - 1
direct/src/fsm/FSM.py

@@ -310,7 +310,7 @@ class FSM(DirectObject):
                 self.name, request, str(args)[1:]))
 
             filter = self.getCurrentFilter()
-            result = list(filter(request, args))
+            result = filter(request, args)
             if result:
                 if isinstance(result, str):
                     # If the return value is a string, it's just the name

+ 24 - 25
direct/src/gui/DirectScrolledList.py

@@ -8,7 +8,6 @@ from direct.directnotify import DirectNotifyGlobal
 from direct.task.Task import Task
 from .DirectFrame import *
 from .DirectButton import *
-import types
 
 
 class DirectScrolledListItem(DirectButton):
@@ -49,7 +48,7 @@ class DirectScrolledList(DirectFrame):
     def __init__(self, parent = None, **kw):
         assert self.notify.debugStateCall(self)
         self.index = 0
-        self.forceHeight = None
+        self.__forceHeight = None
 
         """ If one were to want a scrolledList that makes and adds its items
            as needed, simply pass in an items list of strings (type 'str')
@@ -115,12 +114,12 @@ class DirectScrolledList(DirectFrame):
 
     def setForceHeight(self):
         assert self.notify.debugStateCall(self)
-        self.forceHeight = self["forceHeight"]
+        self.__forceHeight = self["forceHeight"]
 
     def recordMaxHeight(self):
         assert self.notify.debugStateCall(self)
-        if self.forceHeight is not None:
-            self.maxHeight = self.forceHeight
+        if self.__forceHeight is not None:
+            self.maxHeight = self.__forceHeight
         else:
             self.maxHeight = 0.0
             for item in self["items"]:
@@ -130,24 +129,24 @@ class DirectScrolledList(DirectFrame):
     def setScrollSpeed(self):
         assert self.notify.debugStateCall(self)
         # Items per second to move
-        self.scrollSpeed = self["scrollSpeed"]
-        if self.scrollSpeed <= 0:
-            self.scrollSpeed = 1
+        self.__scrollSpeed = self["scrollSpeed"]
+        if self.__scrollSpeed <= 0:
+            self.__scrollSpeed = 1
 
     def setNumItemsVisible(self):
         assert self.notify.debugStateCall(self)
         # Items per second to move
-        self.numItemsVisible = self["numItemsVisible"]
+        self.__numItemsVisible = self["numItemsVisible"]
 
     def destroy(self):
         assert self.notify.debugStateCall(self)
         taskMgr.remove(self.taskName("scroll"))
         if hasattr(self, "currentSelected"):
             del self.currentSelected
-        if self.incButtonCallback:
-            self.incButtonCallback = None
-        if self.decButtonCallback:
-            self.decButtonCallback = None
+        if self.__incButtonCallback:
+            self.__incButtonCallback = None
+        if self.__decButtonCallback:
+            self.__decButtonCallback = None
         self.incButton.destroy()
         self.decButton.destroy()
         DirectFrame.destroy(self)
@@ -169,10 +168,10 @@ class DirectScrolledList(DirectFrame):
         #for i in range(len(self["items"])):
         #    print "buttontext[", i,"]", self["items"][i]["text"]
 
-        if(len(self["items"])==0):
+        if len(self["items"]) == 0:
             return 0
 
-        if(type(self["items"][0])!=types.InstanceType):
+        if type(self["items"][0]) == type(''):
             self.notify.warning("getItemIndexForItemID: cant find itemID for non-class list items!")
             return 0
 
@@ -309,7 +308,7 @@ class DirectScrolledList(DirectFrame):
     def __incButtonDown(self, event):
         assert self.notify.debugStateCall(self)
         task = Task(self.__scrollByTask)
-        task.setDelay(1.0 / self.scrollSpeed)
+        task.setDelay(1.0 / self.__scrollSpeed)
         task.prevTime = 0.0
         task.delta = 1
         taskName = self.taskName("scroll")
@@ -317,13 +316,13 @@ class DirectScrolledList(DirectFrame):
         taskMgr.add(task, taskName)
         self.scrollBy(task.delta)
         messenger.send('wakeup')
-        if self.incButtonCallback:
-            self.incButtonCallback()
+        if self.__incButtonCallback:
+            self.__incButtonCallback()
 
     def __decButtonDown(self, event):
         assert self.notify.debugStateCall(self)
         task = Task(self.__scrollByTask)
-        task.setDelay(1.0 / self.scrollSpeed)
+        task.setDelay(1.0 / self.__scrollSpeed)
         task.prevTime = 0.0
         task.delta = -1
         taskName = self.taskName("scroll")
@@ -331,8 +330,8 @@ class DirectScrolledList(DirectFrame):
         taskMgr.add(task, taskName)
         self.scrollBy(task.delta)
         messenger.send('wakeup')
-        if self.decButtonCallback:
-            self.decButtonCallback()
+        if self.__decButtonCallback:
+            self.__decButtonCallback()
 
     def __buttonUp(self, event):
         assert self.notify.debugStateCall(self)
@@ -345,7 +344,7 @@ class DirectScrolledList(DirectFrame):
         Add this string and extraArg to the list
         """
         assert self.notify.debugStateCall(self)
-        if(type(item) == types.InstanceType):
+        if type(item) != type(''):
             # cant add attribs to non-classes (like strings & ints)
             item.itemID = self.nextItemID
             self.nextItemID += 1
@@ -354,7 +353,7 @@ class DirectScrolledList(DirectFrame):
             item.reparentTo(self.itemFrame)
         if refresh:
             self.refresh()
-        if(type(item) == types.InstanceType):
+        if type(item) != type(''):
             return item.itemID  # to pass to scrollToItemID
 
     def removeItem(self, item, refresh=1):
@@ -466,11 +465,11 @@ class DirectScrolledList(DirectFrame):
 
     def setIncButtonCallback(self):
         assert self.notify.debugStateCall(self)
-        self.incButtonCallback = self["incButtonCallback"]
+        self.__incButtonCallback = self["incButtonCallback"]
 
     def setDecButtonCallback(self):
         assert self.notify.debugStateCall(self)
-        self.decButtonCallback = self["decButtonCallback"]
+        self.__decButtonCallback = self["decButtonCallback"]
 
 
 """

+ 1 - 2
direct/src/gui/OnscreenText.py

@@ -4,7 +4,6 @@ __all__ = ['OnscreenText', 'Plain', 'ScreenTitle', 'ScreenPrompt', 'NameConfirm'
 
 from panda3d.core import *
 from . import DirectGuiGlobals as DGG
-from direct.showbase.DirectObject import DirectObject
 import sys
 
 ## These are the styles of text we might commonly see.  They set the
@@ -17,7 +16,7 @@ ScreenPrompt = 3
 NameConfirm = 4
 BlackOnWhite = 5
 
-class OnscreenText(DirectObject, NodePath):
+class OnscreenText(NodePath):
 
     def __init__(self, text = '',
                  style = Plain,

+ 16 - 16
direct/src/interval/MetaInterval.py

@@ -268,7 +268,7 @@ class MetaInterval(CMetaInterval):
             self.addInterval(ival, maxDuration - ival.getDuration(), TRACK_START)
         self.popLevel(duration)
 
-    def addTrack(self, list, name, relTime, relTo, duration):
+    def addTrack(self, trackList, name, relTime, relTo, duration):
         # Adds a "track list".  This is a list of tuples of the form:
         #
         #   (<delay>, <Interval>,
@@ -281,19 +281,19 @@ class MetaInterval(CMetaInterval):
         # (TRACK_START).  If the relative code is omitted, the default
         # is TRACK_START.
         self.pushLevel(name, relTime, relTo)
-        for tuple in list:
-            if isinstance(tuple, tuple) or \
-               isinstance(tuple, list):
-                relTime = tuple[0]
-                ival = tuple[1]
-                if len(tuple) >= 3:
-                    relTo = tuple[2]
+        for tupleObj in trackList:
+            if isinstance(tupleObj, tuple) or \
+               isinstance(tupleObj, list):
+                relTime = tupleObj[0]
+                ival = tupleObj[1]
+                if len(tupleObj) >= 3:
+                    relTo = tupleObj[2]
                 else:
                     relTo = TRACK_START
                 self.addInterval(ival, relTime, relTo)
 
             else:
-                self.notify.error("Not a tuple in Track: %s" % (tuple,))
+                self.notify.error("Not a tuple in Track: %s" % (tupleObj,))
         self.popLevel(duration)
 
     def addInterval(self, ival, relTime, relTo):
@@ -593,22 +593,22 @@ class Track(MetaInterval):
         meta.addTrack(self.ivals, self.getName(),
                       relTime, relTo, self.phonyDuration)
 
-    def validateComponent(self, tuple):
+    def validateComponent(self, tupleObj):
         # This is called only in debug mode to verify that the
         # indicated component added to the MetaInterval is appropriate
         # to this type of MetaInterval.  In most cases except Track,
         # this is the same as asking that the component is itself an
         # Interval.
 
-        if not (isinstance(tuple, tuple) or \
-                isinstance(tuple, list)):
+        if not (isinstance(tupleObj, tuple) or \
+                isinstance(tupleObj, list)):
             # It's not a tuple.
             return 0
 
-        relTime = tuple[0]
-        ival = tuple[1]
-        if len(tuple) >= 3:
-            relTo = tuple[2]
+        relTime = tupleObj[0]
+        ival = tupleObj[1]
+        if len(tupleObj) >= 3:
+            relTo = tupleObj[2]
         else:
             relTo = TRACK_START
 

+ 1 - 0
direct/src/showbase/ShowBaseGlobal.py

@@ -20,6 +20,7 @@ def inspect(anObject):
     Inspector = importlib.import_module('direct.tkpanels.Inspector')
     return Inspector.inspect(anObject)
 
+import sys
 if sys.version_info >= (3, 0):
     import builtins
 else:

+ 0 - 374
direct/src/showbase/pandaSqueezeTool.py

@@ -1,374 +0,0 @@
-"""Undocumented Module"""
-
-__all__ = ['usage', 'Squeezer', 'Loader', 'boot', 'open', 'explode', 'getloader', 'squeeze', 'searchPath']
-
-#!/usr/bin/env python
-#
-# SQUEEZE
-#
-# squeeze a python program
-#
-# installation:
-# - use this script as is, or squeeze it using the following command:
-#
-# python squeezeTool.py -1su -o squeeze -b squeezeTool squeezeTool.py
-#
-# notes:
-# - this is pretty messy.  make sure to test everything carefully
-#   if you change anything
-#
-# - the name "squeeze" is taken from an ABC800 utility which did
-#   about the same thing with Basic II bytecodes.
-#
-# history:
-# 1.0   97-04-22 fl     Created
-# 1.1   97-05-25 fl     Added base64 embedding option (-1)
-#       97-05-25 fl     Check for broken package file
-# 1.2   97-05-26 fl     Support uncompressed packages (-u)
-# 1.3   97-05-27 fl     Check byte code magic, eliminated StringIO, etc.
-# 1.4   97-06-04 fl     Removed last bits of white space, removed try/except
-# 1.5   97-06-17 fl     Added squeeze archive capabilities (-x)
-# 1.6   98-05-04 fl     Minor fixes in preparation for public source release
-#
-# reviews:
-#       "Fredrik Lundh is a friggin genius"
-#       -- Aaron Watters, author of 'Internet Programming with Python'
-#
-#       "I agree ... this is a friggin Good Thing"
-#       -- Paul Everitt, Digital Creations
-#
-# Copyright (c) 1997 by Fredrik Lundh.
-# Copyright (c) 1997-1998 by Secret Labs AB
-#
-# [email protected]
-# http://www.pythonware.com
-#
-# --------------------------------------------------------------------
-# Permission to use, copy, modify, and distribute this software and
-# its associated documentation for any purpose and without fee is
-# hereby granted.  This software is provided as is.
-# --------------------------------------------------------------------
-
-VERSION = "1.6/98-05-04"
-MAGIC   = "[PANDASQUEEZE]"
-
-import base64, imp, marshal, os, sys
-
-# --------------------------------------------------------------------
-# usage
-
-def usage():
-        print("\nSQUEEZE", VERSION, "(c) 1997-1998 by Secret Labs AB")
-        print("""\
-Convert a Python application to a compressed module package.
-
-Usage: squeeze [-1ux] -o app [-b start] modules... [-d files...]
-
-This utility creates a compressed package file named "app.pyz", which
-contains the given module files.  It also creates a bootstrap script
-named "app.py", which loads the package and imports the given "start"
-module to get things going.  Example:
-
-        squeeze -o app -b appMain app*.py
-
-The -1 option tells squeeze to put the package file inside the boot-
-strap script using base64 encoding.  The result is a single text file
-containing the full application.
-
-The -u option disables compression.  Otherwise, the package will be
-compressed using zlib, and the user needs zlib to run the resulting
-application.
-
-The -d option can be used to put additional files in the package file.
-You can access these files via "__main__.open(filename)" (returns a
-StringIO file object).
-
-The -x option can be used with -d to create a self-extracting archive,
-instead of a package.  When the resulting script is executed, the
-data files are extracted.  Omit the -b option in this case.
-""")
-        sys.exit(1)
-
-
-# --------------------------------------------------------------------
-# squeezer -- collect squeezed modules
-
-class Squeezer:
-
-        def __init__(self):
-
-                self.rawbytes = self.bytes = 0
-                self.modules = {}
-
-        def addmodule(self, file):
-
-                if file[-1] == "c":
-                        file = file[:-1]
-
-                m = os.path.splitext(os.path.split(file)[1])[0]
-
-                # read sourcefile
-                f = open(file)
-                codestring = f.read()
-                f.close()
-
-                # dump to file
-                self.modules[m] = compile(codestring, m, "exec")
-
-        def adddata(self, file):
-
-                self.modules["+"+file] = open(file, "rb").read()
-
-        def getarchive(self):
-
-                # marshal our module dictionary
-                data = marshal.dumps(self.modules)
-                self.rawbytes = len(data)
-
-                # return (compressed) dictionary
-                data = zlib.compress(data, 9)
-                self.bytes = len(data)
-
-                return data
-
-        def getstatus(self):
-                return self.bytes, self.rawbytes
-
-
-# --------------------------------------------------------------------
-# loader (used in bootstrap code)
-
-loader = """
-import ihooks
-
-PYZ_MODULE = 64
-
-class Loader(ihooks.ModuleLoader):
-
-    def __init__(self, modules):
-        self.__modules = modules
-        return ihooks.ModuleLoader.__init__(self)
-
-    def find_module(self, name, path = None):
-        try:
-            self.__modules[name]
-            return None, None, (None, None, PYZ_MODULE)
-        except KeyError:
-            return ihooks.ModuleLoader.find_module(self, name, path)
-
-    def load_module(self, name, stuff):
-        file, filename, (suff, mode, type) = stuff
-        if type != PYZ_MODULE:
-            return ihooks.ModuleLoader.load_module(self, name, stuff)
-        #print "PYZ:", "import", name
-        basename = name.split('.')[-1]
-        code = self.__modules[basename]
-        del self.__modules[basename] # no need to keep this one around
-        m = self.hooks.add_module(name)
-        m.__file__ = filename
-        exec code in m.__dict__
-        return m
-
-def boot(name, fp, size, offset = 0):
-
-    global data
-
-    try:
-        import %(modules)s
-    except ImportError:
-        #print "PYZ:", "failed to load marshal and zlib libraries"
-        return # cannot boot from PYZ file
-    #print "PYZ:", "boot from", name+".PYZ"
-
-    # load archive and install import hook
-    if offset:
-        data = fp[offset:]
-    else:
-        data = fp.read(size)
-        fp.close()
-
-    if len(data) != size:
-        raise IOError, "package is truncated"
-
-    data = marshal.loads(%(data)s)
-
-    ihooks.install(ihooks.ModuleImporter(Loader(data)))
-"""
-
-loaderopen = """
-
-def open(name):
-    from io import StringIO
-    try:
-        return StringIO(data["+"+name])
-    except KeyError:
-        raise IOError, (0, "no such file")
-"""
-
-loaderexplode = """
-
-def explode():
-    for k, v in data.items():
-        if k[0] == "+":
-            try:
-                open(k[1:], "wb").write(v)
-                print k[1:], "extracted ok"
-            except IOError, v:
-                print k[1:], "failed:", "IOError", v
-
-"""
-
-def getloader(data, package):
-
-        s = loader
-
-        if data:
-                if explode:
-                        s = s + loaderexplode
-                else:
-                        s = s + loaderopen
-
-        dict = {
-                "modules": "marshal, zlib",
-                "data":    "zlib.decompress(data)",
-                }
-
-        s = s % dict
-
-        return marshal.dumps(compile(s, "<package>", "exec"))
-
-
-# --------------------------------------------------------------------
-# Main
-# --------------------------------------------------------------------
-
-#
-# parse options
-
-import sys
-import zlib
-
-embed = 0
-explode = 0
-
-def squeeze(app, start, filelist, outputDir):
-        localMagic = MAGIC
-        data = None
-
-        bootstrap = os.path.join(outputDir, app + ".py")
-        archiveBase = app + ".pyz"
-        archive   = os.path.join(outputDir, archiveBase)
-
-        archiveid = app
-
-        #
-        # avoid overwriting files not generated by squeeze
-
-        try:
-                fp = open(bootstrap)
-                s = fp.readline()
-                s.index(MAGIC)
-        except IOError:
-                pass
-        except ValueError:
-                print("%s was not created by squeeze.  You have to manually" % (bootstrap))
-                print("remove the file to proceed.")
-                sys.exit(1)
-
-        #
-        # collect modules
-
-        sq = Squeezer()
-        for file in filelist:
-                # print 'addmodule:', file
-                sq.addmodule(file)
-
-        package = sq.getarchive()
-        size = len(package)
-
-        #
-        # get loader
-
-        loader = getloader(data, package)
-
-        zbegin, zend = "zlib.decompress(", ")"
-        loader = zlib.compress(loader, 9)
-
-        loaderlen = len(loader)
-
-        magic = repr(imp.get_magic())
-        version = sys.version.split()[0]
-
-        #
-        # generate script and package files
-
-        if embed:
-
-                # embedded archive
-                data = base64.encodestring(loader + package)
-
-                fp = open(bootstrap, "w")
-                fp.write('''\
-#%(localMagic)s %(archiveid)s
-import ihooks, zlib, base64, marshal
-s=base64.decodestring("""
-%(data)s""")
-exec marshal.loads(%(zbegin)ss[:%(loaderlen)d]%(zend)s)
-boot("%(app)s", s, %(size)d, %(loaderlen)d)
-exec "import %(start)s"
-''' % locals())
-                bytes = fp.tell()
-
-        else:
-
-                # separate archive file
-
-                fp = open(archive, "wb")
-
-                fp.write(loader)
-                fp.write(package)
-
-                bytes = fp.tell()
-                fp.close()
-                #
-                # create bootstrap code
-
-                fp = open(bootstrap, "w")
-                # Note: David Rose adjusted the following to be panda-specific.
-                fp.write("""\
-#%(localMagic)s %(archiveid)s
-import ihooks, zlib, marshal, os, sys
-
-def searchPath(filename):
-  # Look along panda3d.__path__ for the indicated filename.  Returns
-  # the located pathname, or None if the filename is not found.
-  import panda3d
-
-  for dir in panda3d.__path__:
-    pathname = os.path.join(dir, filename)
-    if os.path.exists(pathname):
-      return pathname
-
-  return None
-
-# Look for %(archiveBase)s along panda3d.__path__.
-archiveName = "%(archiveBase)s"
-archivePath = searchPath(archiveName)
-if archivePath == None:
-  raise ImportError, "Could not locate panda3d.%%s." %% (archiveName)
-
-f=open(archivePath,"rb")
-exec marshal.loads(%(zbegin)sf.read(%(loaderlen)d)%(zend)s)
-boot("%(app)s", f, %(size)d)
-exec "from %(start)s import *"
-#exec "run()"
-""" % locals())
-                bytes = bytes + fp.tell()
-                fp.close()
-
-        #
-        # show statistics
-
-        dummy, rawbytes = sq.getstatus()
-
-        print("squeezed %s to %s (%d%%)" % (rawbytes, bytes, bytes * 100 / rawbytes))

+ 0 - 57
direct/src/showbase/pandaSqueezer.py

@@ -1,57 +0,0 @@
-"""Undocumented Module"""
-
-__all__ = []
-
-import os
-import sys
-import getopt
-from . import pandaSqueezeTool
-
-# Assumption: We will be squeezing the files from the current directory or the -d directory.
-
-if __name__ == "__main__":
-    try:
-        opts, pargs = getopt.getopt(sys.argv[1:], 'Od:')
-    except Exception as e:
-        # User passed in a bad option, print the error and the help, then exit
-        print(e)
-        print('Usage: pass in -O for optimized')
-        print('       pass in -d directory')
-        sys.exit()
-
-    fOptimized = 0
-    # Store the option values into our variables
-    for opt in opts:
-        flag, value = opt
-        if (flag == '-O'):
-            fOptimized = 1
-            print('Squeezing pyo files')
-        elif (flag == '-d'):
-            os.chdir(value)
-
-    def getSqueezeableFiles():
-        fileList = os.listdir(".")
-        newFileList = []
-        if fOptimized:
-            targetFileExtension = ".pyo"
-        else:
-            targetFileExtension = ".pyc"
-        for i in fileList:
-            base, ext = os.path.splitext(i)
-            if (ext == ".py"):
-                newFileList.append(i)
-        return newFileList
-
-    def squeezePandaFiles():
-        l = getSqueezeableFiles()
-        pandaSqueezeTool.squeeze("PandaModules", "PandaModulesUnsqueezed", l)
-
-        # Clean up the source files now that they've been squeezed.  If
-        # you don't like this behavior (e.g. if you want to inspect the
-        # generated files), use genPyCode -n to avoid squeezing
-        # altogether.
-        for i in l:
-            os.unlink(i)
-
-
-    squeezePandaFiles()

+ 17 - 3
dtool/src/dtoolbase/pvector.h

@@ -39,11 +39,25 @@ public:
   typedef vector<Type, allocator> base_class;
   typedef TYPENAME base_class::size_type size_type;
 
-  pvector(TypeHandle type_handle = pvector_type_handle) : base_class(allocator(type_handle)) { }
+  explicit pvector(TypeHandle type_handle = pvector_type_handle) : base_class(allocator(type_handle)) { }
   pvector(const pvector<Type> &copy) : base_class(copy) { }
-  pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { }
-  pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { }
+  explicit pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { }
+  explicit pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { }
   pvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator(type_handle)) { }
+
+#ifdef USE_MOVE_SEMANTICS
+  pvector(pvector<Type> &&from) NOEXCEPT : base_class(move(from)) {};
+
+  pvector<Type> &operator =(pvector<Type> &&from) NOEXCEPT {
+    base_class::operator =(move(from));
+    return *this;
+  }
+#endif
+
+  pvector<Type> &operator =(const pvector<Type> &copy) {
+    base_class::operator =(copy);
+    return *this;
+  }
 };
 
 #endif  // USE_STL_ALLOCATOR

+ 3 - 2
dtool/src/dtoolbase/typeRegistry.h

@@ -37,14 +37,15 @@ class EXPCL_DTOOL TypeRegistry : public MemoryBase {
 public:
   // User code shouldn't generally need to call TypeRegistry::register_type()
   // or record_derivation() directly; instead, use the register_type
-  // convenience function, defined below.
+  // convenience function, defined in register_type.h.
   bool register_type(TypeHandle &type_handle, const string &name);
+
+PUBLISHED:
   TypeHandle register_dynamic_type(const string &name);
 
   void record_derivation(TypeHandle child, TypeHandle parent);
   void record_alternate_name(TypeHandle type, const string &name);
 
-PUBLISHED:
   TypeHandle find_type(const string &name) const;
   TypeHandle find_type_by_id(int id) const;
 

+ 14 - 0
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -1212,6 +1212,10 @@ write_sub_module(ostream &out, Object *obj) {
       out << "  assert(" << class_ptr << " != NULL);\n";
     } else {
       class_ptr = "&Dtool_" + class_name;
+
+      // If this is a typedef to a class defined in the same module, make sure
+      // that the class is initialized before we try to define the typedef.
+      out << "  Dtool_PyModuleClassInit_" << class_name << "(module);\n";
     }
   }
 
@@ -2692,6 +2696,12 @@ write_module_class(ostream &out, Object *obj) {
   out << "#if PY_VERSION_HEX >= 0x02050000\n";
   write_function_slot(out, 2, slots, "nb_index");
   out << "#endif\n";
+
+  out << "#if PY_VERSION_HEX >= 0x03050000\n";
+  write_function_slot(out, 2, slots, "nb_matrix_multiply");
+  write_function_slot(out, 2, slots, "nb_inplace_matrix_multiply");
+  out << "#endif\n";
+
   out << "};\n\n";
 
   // NB: it's tempting not to write this table when a class doesn't have them.
@@ -2938,6 +2948,10 @@ write_module_class(ostream &out, Object *obj) {
   out << "#if PY_VERSION_HEX >= 0x02060000\n";
   out << "    0, // tp_version_tag\n";
   out << "#endif\n";
+  // destructor tp_finalize
+  out << "#if PY_VERSION_HEX >= 0x03040000\n";
+  out << "    0, // tp_finalize\n";
+  out << "#endif\n";
   out << "  },\n";
 
   // It's tempting to initialize the type handle here, but this causes static

+ 1 - 0
dtool/src/parser-inc/sys/time.h

@@ -2,3 +2,4 @@
 
 struct timeval;
 struct fd_set;
+struct timezone;

+ 0 - 6
makepanda/installer.nsi

@@ -15,7 +15,6 @@
 ;   BUILT         - location of panda install tree.
 ;   SOURCE        - location of the panda source-tree if available, OR location of panda install tree.
 ;   PYVER         - version of Python that Panda was built with (ie, "2.7")
-;   PYEXTRAS      - directory containing python extras, if any.
 ;   REGVIEW       - either 32 or 64, depending on the build architecture.
 ;
 
@@ -372,11 +371,6 @@ SectionGroup "Python support"
         SetOutPath "$INSTDIR\python"
         File /r "${BUILT}\python\*"
 
-        !ifdef PYEXTRAS
-        SetOutPath "$INSTDIR\python\lib"
-        File /nonfatal /r "${PYEXTRAS}\*"
-        !endif
-
         SetDetailsPrint both
         DetailPrint "Adding registry keys for Python..."
         SetDetailsPrint listonly

+ 22 - 12
makepanda/makepanda.py

@@ -566,6 +566,10 @@ if (COMPILER == "MSVC"):
                 LibName(pkg, 'dxerrVNUM.lib'.replace("VNUM", vnum))
             #LibName(pkg, 'ddraw.lib')
             LibName(pkg, 'dxguid.lib')
+
+    if not PkgSkip("FREETYPE") and os.path.isdir(GetThirdpartyDir() + "freetype/include/freetype2"):
+        IncDirectory("FREETYPE", GetThirdpartyDir() + "freetype/include/freetype2")
+
     IncDirectory("ALWAYS", GetThirdpartyDir() + "extras/include")
     LibName("WINSOCK", "wsock32.lib")
     LibName("WINSOCK2", "wsock32.lib")
@@ -592,17 +596,26 @@ if (COMPILER == "MSVC"):
     if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "quartz.lib")
     if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbc32.lib")
     if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbccp32.lib")
-    if (PkgSkip("PNG")==0):      LibName("PNG",      GetThirdpartyDir() + "png/lib/libpng_static.lib")
+    if (PkgSkip("OPENSSL")==0):
+        LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandassl.lib")
+        LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandaeay.lib")
+    if (PkgSkip("PNG")==0):
+        if os.path.isfile(GetThirdpartyDir() + "png/lib/libpng16_static.lib"):
+            LibName("PNG", GetThirdpartyDir() + "png/lib/libpng16_static.lib")
+        else:
+            LibName("PNG", GetThirdpartyDir() + "png/lib/libpng_static.lib")
+    if (PkgSkip("TIFF")==0):
+        if os.path.isfile(GetThirdpartyDir() + "tiff/lib/libtiff.lib"):
+            LibName("TIFF", GetThirdpartyDir() + "tiff/lib/libtiff.lib")
+        else:
+            LibName("TIFF", GetThirdpartyDir() + "tiff/lib/tiff.lib")
     if (PkgSkip("JPEG")==0):     LibName("JPEG",     GetThirdpartyDir() + "jpeg/lib/jpeg-static.lib")
-    if (PkgSkip("TIFF")==0):     LibName("TIFF",     GetThirdpartyDir() + "tiff/lib/libtiff.lib")
     if (PkgSkip("ZLIB")==0):     LibName("ZLIB",     GetThirdpartyDir() + "zlib/lib/zlibstatic.lib")
     if (PkgSkip("VRPN")==0):     LibName("VRPN",     GetThirdpartyDir() + "vrpn/lib/vrpn.lib")
     if (PkgSkip("VRPN")==0):     LibName("VRPN",     GetThirdpartyDir() + "vrpn/lib/quat.lib")
     if (PkgSkip("NVIDIACG")==0): LibName("CGGL",     GetThirdpartyDir() + "nvidiacg/lib/cgGL.lib")
     if (PkgSkip("NVIDIACG")==0): LibName("CGDX9",    GetThirdpartyDir() + "nvidiacg/lib/cgD3D9.lib")
     if (PkgSkip("NVIDIACG")==0): LibName("NVIDIACG", GetThirdpartyDir() + "nvidiacg/lib/cg.lib")
-    if (PkgSkip("OPENSSL")==0):  LibName("OPENSSL",  GetThirdpartyDir() + "openssl/lib/libpandassl.lib")
-    if (PkgSkip("OPENSSL")==0):  LibName("OPENSSL",  GetThirdpartyDir() + "openssl/lib/libpandaeay.lib")
     if (PkgSkip("FREETYPE")==0): LibName("FREETYPE", GetThirdpartyDir() + "freetype/lib/freetype.lib")
     if (PkgSkip("FFTW")==0):     LibName("FFTW",     GetThirdpartyDir() + "fftw/lib/rfftw.lib")
     if (PkgSkip("FFTW")==0):     LibName("FFTW",     GetThirdpartyDir() + "fftw/lib/fftw.lib")
@@ -708,7 +721,7 @@ if (COMPILER == "MSVC"):
         IncDirectory("SPEEDTREE", SDK["SPEEDTREE"] + "/Include")
     if (PkgSkip("BULLET")==0):
         suffix = '.lib'
-        if GetTargetArch() == 'x64':
+        if GetTargetArch() == 'x64' and os.path.isfile(GetThirdpartyDir() + "bullet/lib/BulletCollision_x64.lib"):
             suffix = '_x64.lib'
         LibName("BULLET", GetThirdpartyDir() + "bullet/lib/LinearMath" + suffix)
         LibName("BULLET", GetThirdpartyDir() + "bullet/lib/BulletCollision" + suffix)
@@ -3414,8 +3427,7 @@ if (not RUNTIME):
   TargetAdd('libp3putil.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libp3putil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3putil', 'SRCDIR:panda/src/putil'])
   TargetAdd('libp3putil_igate.obj', input='libp3putil.in', opts=["DEPENDENCYONLY"])
-  TargetAdd('p3putil_typedWritable_ext.obj', opts=OPTS, input='typedWritable_ext.cxx')
-  TargetAdd('p3putil_pythonCallbackObject.obj', opts=OPTS, input='pythonCallbackObject.cxx')
+  TargetAdd('p3putil_ext_composite.obj', opts=OPTS, input='p3putil_ext_composite.cxx')
 
 #
 # DIRECTORY: panda/src/audio/
@@ -4016,8 +4028,7 @@ if (not RUNTIME):
   if PkgSkip("FREETYPE")==0:
     TargetAdd('core.pyd', input="libp3pnmtext_igate.obj")
 
-  TargetAdd('core.pyd', input='p3putil_typedWritable_ext.obj')
-  TargetAdd('core.pyd', input='p3putil_pythonCallbackObject.obj')
+  TargetAdd('core.pyd', input='p3putil_ext_composite.obj')
   TargetAdd('core.pyd', input='p3pnmimage_pfmFile_ext.obj')
   TargetAdd('core.pyd', input='p3event_pythonTask.obj')
   TargetAdd('core.pyd', input='p3gobj_ext_composite.obj')
@@ -6533,7 +6544,6 @@ def MakeInstallerNSIS(file, title, installdir):
         'BUILT'       : panda,
         'SOURCE'      : psource,
         'PYVER'       : SDK["PYTHONVERSION"][6:9],
-        'PYEXTRAS'    : os.path.join(os.path.abspath(GetThirdpartyBase()), 'win-extras'),
         'REGVIEW'     : regview,
     }
 
@@ -6910,8 +6920,8 @@ def MakeInstallerOSX():
         oscmd("cp -R %s/pandac                dstroot/pythoncode/Developer/Panda3D/pandac" % GetOutputDir())
         oscmd("cp -R %s/direct                dstroot/pythoncode/Developer/Panda3D/direct" % GetOutputDir())
         oscmd("ln -s %s                       dstroot/pythoncode/usr/local/bin/ppython" % SDK["PYTHONEXEC"])
-        oscmd("cp -R %s/*.so                  dstroot/pythoncode/Developer/Panda3D/" % GetOutputDir())
-        oscmd("cp -R %s/*.py                  dstroot/pythoncode/Developer/Panda3D/" % GetOutputDir())
+        oscmd("cp -R %s/*.so                  dstroot/pythoncode/Developer/Panda3D/" % GetOutputDir(), True)
+        oscmd("cp -R %s/*.py                  dstroot/pythoncode/Developer/Panda3D/" % GetOutputDir(), True)
         if os.path.isdir(GetOutputDir()+"/Pmw"):
             oscmd("cp -R %s/Pmw               dstroot/pythoncode/Developer/Panda3D/Pmw" % GetOutputDir())
             compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/Pmw")

+ 49 - 19
makepanda/makepandacore.py

@@ -1122,12 +1122,12 @@ def GetThirdpartyDir():
     target_arch = GetTargetArch()
 
     if (target == 'windows'):
+        vc = SDK["VISUALSTUDIO_VERSION"].split('.')[0]
+
         if target_arch == 'x64':
-            THIRDPARTYDIR = base + "/win-libs-vc10-x64/"
-            if not os.path.isdir(THIRDPARTYDIR):
-                THIRDPARTYDIR = base + "/win-libs-vc10/"
+            THIRDPARTYDIR = base + "/win-libs-vc" + vc + "-x64/"
         else:
-            THIRDPARTYDIR = base + "/win-libs-vc10/"
+            THIRDPARTYDIR = base + "/win-libs-vc" + vc + "/"
 
     elif (target == 'darwin'):
         # OSX thirdparty binaries are universal, where possible.
@@ -1409,10 +1409,16 @@ def PkgConfigEnable(opt, pkgname, tool = "pkg-config"):
     for i, j in PkgConfigGetDefSymbols(pkgname, tool).items():
         DefSymbol(opt, i, j)
 
-def LocateLibrary(lib, lpath=[]):
-    """ Returns True if this library was found in the given search path, False otherwise. """
+def LocateLibrary(lib, lpath=[], prefer_static=False):
+    """Searches for the library in the search path, returning its path if found,
+    or None if it was not found."""
     target = GetTarget()
 
+    if prefer_static and target != 'windows':
+        for dir in lpath:
+            if os.path.isfile(os.path.join(dir, 'lib%s.a' % lib)):
+                return os.path.join(dir, 'lib%s.a' % lib)
+
     for dir in lpath:
         if target == 'darwin' and os.path.isfile(os.path.join(dir, 'lib%s.dylib' % lib)):
             return os.path.join(dir, 'lib%s.dylib' % lib)
@@ -1484,6 +1490,7 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
         LibName(target_pkg, "-lswresample")
         return
 
+    # First check if the package is in the thirdparty directory.
     pkg_dir = os.path.join(GetThirdpartyDir(), pkg.lower())
     if not custom_loc and os.path.isdir(pkg_dir):
         if framework and os.path.isdir(os.path.join(pkg_dir, framework + ".framework")):
@@ -1494,32 +1501,55 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
         if os.path.isdir(os.path.join(pkg_dir, "include")):
             IncDirectory(target_pkg, os.path.join(pkg_dir, "include"))
 
-        if os.path.isdir(os.path.join(pkg_dir, "lib")):
-            LibDirectory(target_pkg, os.path.join(pkg_dir, "lib"))
+            # Handle cases like freetype2 where the include dir is a subdir under "include"
+            for i in incs:
+                if os.path.isdir(os.path.join(pkg_dir, "include", i)):
+                    IncDirectory(target_pkg, os.path.join(pkg_dir, "include", i))
+
+        lpath = [os.path.join(pkg_dir, "lib")]
 
-        if (PkgSkip("PYTHON") == 0):
+        if not PkgSkip("PYTHON"):
             py_lib_dir = os.path.join(pkg_dir, "lib", SDK["PYTHONVERSION"])
             if os.path.isdir(py_lib_dir):
-                LibDirectory(target_pkg, py_lib_dir)
+                lpath.append(py_lib_dir)
 
-        # TODO: check for a .pc file in the lib/pkg-config/ dir
+        # TODO: check for a .pc file in the lib/pkgconfig/ dir
         if (tool != None and os.path.isfile(os.path.join(pkg_dir, "bin", tool))):
             tool = os.path.join(pkg_dir, "bin", tool)
             for i in PkgConfigGetLibs(None, tool):
-                LibName(target_pkg, i)
+                if i.startswith('-l'):
+                    # To make sure we don't pick up the system copy, write out
+                    # the full path instead.
+                    libname = i[2:]
+                    location = LocateLibrary(libname, lpath, prefer_static=True)
+                    if location is not None:
+                        LibName(target_pkg, location)
+                    else:
+                        print(GetColor("cyan") + "Couldn't find library lib" + libname + " in thirdparty directory " + pkg.lower() + GetColor())
+                        LibName(target_pkg, i)
+                else:
+                    LibName(target_pkg, i)
             for i, j in PkgConfigGetDefSymbols(None, tool).items():
                 DefSymbol(target_pkg, i, j)
             return
 
+        # Now search for the libraries in the package's lib directories.
         for l in libs:
             libname = l
             if l.startswith("lib"):
                 libname = l[3:]
-            # This is for backward compatibility - in the thirdparty dir, we kept some libs with "panda" prefix, like libpandatiff.
-            if len(glob.glob(os.path.join(pkg_dir, "lib", "libpanda%s.*" % (libname)))) > 0 \
-               and len(glob.glob(os.path.join(pkg_dir, "lib", "lib%s.*" % (libname)))) == 0:
-                libname = "panda" + libname
-            LibName(target_pkg, "-l" + libname)
+
+            location = LocateLibrary(libname, lpath, prefer_static=True)
+            if location is not None:
+                LibName(target_pkg, location)
+            else:
+                # This is for backward compatibility - in the thirdparty dir,
+                # we kept some libs with "panda" prefix, like libpandatiff.
+                location = LocateLibrary("panda" + libname, lpath, prefer_static=True)
+                if location is not None:
+                    LibName(target_pkg, location)
+                else:
+                    print(GetColor("cyan") + "Couldn't find library lib" + libname + " in thirdparty directory " + pkg.lower() + GetColor())
 
         for d, v in defs.values():
             DefSymbol(target_pkg, d, v)
@@ -2299,8 +2329,8 @@ def SetupVisualStudioEnviron():
         AddToPathEnv("PATH",    SDK["MSPLATFORM"] + "bin\\" + arch)
 
         # Windows Kit 10 introduces the "universal CRT".
-        inc_dir = SDK["MSPLATFORM"] + "Include\\10.0.10240.0\\"
-        lib_dir = SDK["MSPLATFORM"] + "Lib\\10.0.10240.0\\"
+        inc_dir = SDK["MSPLATFORM"] + "Include\\10.0.10586.0\\"
+        lib_dir = SDK["MSPLATFORM"] + "Lib\\10.0.10586.0\\"
         AddToPathEnv("INCLUDE", inc_dir + "shared")
         AddToPathEnv("INCLUDE", inc_dir + "ucrt")
         AddToPathEnv("INCLUDE", inc_dir + "um")

+ 1 - 1
panda/src/bullet/bulletContactResult.I

@@ -86,7 +86,7 @@ get_num_contacts() const {
 /**
  *
  */
-INLINE BulletContact &BulletContactResult::
+INLINE BulletContact BulletContactResult::
 get_contact(int idx) {
 
   nassertr(idx >= 0 && idx < (int)_contacts.size(), _empty);

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

@@ -62,7 +62,7 @@ struct EXPCL_PANDABULLET BulletContactResult : public btCollisionWorld::ContactR
 
 PUBLISHED:
   INLINE int get_num_contacts() const;
-  INLINE BulletContact &get_contact(int idx);
+  INLINE BulletContact get_contact(int idx);
   MAKE_SEQ(get_contacts, get_num_contacts, get_contact);
 
 public:

+ 1 - 1
panda/src/bullet/bulletHeightfieldShape.I

@@ -18,7 +18,7 @@ INLINE BulletHeightfieldShape::
 ~BulletHeightfieldShape() {
 
   delete _shape;
-  delete _data;
+  delete [] _data;
 }
 
 /**

+ 4 - 0
panda/src/bullet/bulletTriangleMesh.cxx

@@ -134,6 +134,8 @@ add_geom(const Geom *geom, bool remove_duplicate_vertices, const TransformState
       _mesh->addTriangle(v0, v1, v2, remove_duplicate_vertices);
     }
   }
+
+  delete [] vertices;
 }
 
 /**
@@ -163,6 +165,8 @@ add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_dupli
 
     _mesh->addTriangle(v0, v1, v2, remove_duplicate_vertices);
   }
+
+  delete [] vertices;
 }
 
 /**

+ 10 - 4
panda/src/chan/animChannelMatrixXfmTable.cxx

@@ -327,10 +327,16 @@ void AnimChannelMatrixXfmTable::
 write_datagram(BamWriter *manager, Datagram &me) {
   AnimChannelMatrix::write_datagram(manager, me);
 
-  if (compress_channels && !FFTCompressor::is_compression_available()) {
-    chan_cat.error()
-      << "Compression is not available; writing uncompressed channels.\n";
-    compress_channels = false;
+  if (compress_channels) {
+    chan_cat.warning()
+      << "FFT compression of animations is deprecated.  For compatibility "
+         "with future versions of Panda3D, set compress-channels to false.\n";
+
+    if (!FFTCompressor::is_compression_available()) {
+      chan_cat.error()
+        << "Compression is not available; writing uncompressed channels.\n";
+      compress_channels = false;
+    }
   }
 
   me.add_bool(compress_channels);

+ 10 - 4
panda/src/chan/animChannelScalarTable.cxx

@@ -146,10 +146,16 @@ void AnimChannelScalarTable::
 write_datagram(BamWriter *manager, Datagram &me) {
   AnimChannelScalar::write_datagram(manager, me);
 
-  if (compress_channels && !FFTCompressor::is_compression_available()) {
-    chan_cat.error()
-      << "Compression is not available; writing uncompressed channels.\n";
-    compress_channels = false;
+  if (compress_channels) {
+    chan_cat.warning()
+      << "FFT compression of animations is deprecated.  For compatibility "
+         "with future versions of Panda3D, set compress-channels to false.\n";
+
+    if (!FFTCompressor::is_compression_available()) {
+      chan_cat.error()
+        << "Compression is not available; writing uncompressed channels.\n";
+      compress_channels = false;
+    }
   }
 
   me.add_bool(compress_channels);

+ 11 - 0
panda/src/chan/config_chan.cxx

@@ -138,4 +138,15 @@ ConfigureFn(config_chan) {
   AnimChannelScalarTable::register_with_read_factory();
   AnimChannelScalarDynamic::register_with_read_factory();
   AnimPreloadTable::register_with_read_factory();
+
+  // For compatibility with old .bam files.
+#ifndef STDFLOAT_DOUBLE
+  TypeRegistry *reg = TypeRegistry::ptr();
+  reg->record_alternate_name(AnimChannelFixed<ACMatrixSwitchType>::get_class_type(),
+                             "AnimChannelFixed<LMatrix4f>");
+  reg->record_alternate_name(MovingPart<ACMatrixSwitchType>::get_class_type(),
+                             "MovingPart<LMatrix4f>");
+  reg->record_alternate_name(MovingPart<ACScalarSwitchType>::get_class_type(),
+                             "MovingPart<float>");
+#endif
 }

+ 9 - 9
panda/src/display/drawableRegion.I

@@ -17,10 +17,10 @@
 INLINE DrawableRegion::
 DrawableRegion() :
   _screenshot_buffer_type(RenderBuffer::T_front),
-  _draw_buffer_type(RenderBuffer::T_back)
+  _draw_buffer_type(RenderBuffer::T_back),
+  _clear_mask(0)
 {
-  for (int i=0; i<RTP_COUNT; i++) {
-    _clear_active[i] = false;
+  for (int i = 0; i < RTP_COUNT; ++i) {
     _clear_value[i] = LColor(0.0f, 0.0f, 0.0f, 0.0f);
   }
   _clear_value[RTP_depth] = LColor(1.0f,1.0f,1.0f,1.0f);
@@ -35,11 +35,11 @@ INLINE DrawableRegion::
 DrawableRegion(const DrawableRegion &copy) :
   _screenshot_buffer_type(copy._screenshot_buffer_type),
   _draw_buffer_type(copy._draw_buffer_type),
+  _clear_mask(copy._clear_mask),
   _pixel_zoom(copy._pixel_zoom),
   _pixel_factor(copy._pixel_factor)
 {
-  for (int i=0; i<RTP_COUNT; i++) {
-    _clear_active[i] = copy._clear_active[i];
+  for (int i = 0; i < RTP_COUNT; ++i) {
     _clear_value[i] = copy._clear_value[i];
   }
 }
@@ -51,8 +51,8 @@ INLINE void DrawableRegion::
 operator = (const DrawableRegion &copy) {
   _screenshot_buffer_type = copy._screenshot_buffer_type;
   _draw_buffer_type = copy._draw_buffer_type;
-  for (int i=0; i<RTP_COUNT; i++) {
-    _clear_active[i] = copy._clear_active[i];
+  _clear_mask = copy._clear_mask;
+  for (int i = 0; i < RTP_COUNT; ++i) {
     _clear_value[i] = copy._clear_value[i];
   }
   _pixel_zoom = copy._pixel_zoom;
@@ -64,8 +64,8 @@ operator = (const DrawableRegion &copy) {
  */
 INLINE void DrawableRegion::
 copy_clear_settings(const DrawableRegion &copy) {
-  for (int i=0; i<RTP_COUNT; i++) {
-    _clear_active[i] = copy._clear_active[i];
+  _clear_mask = copy._clear_mask;
+  for (int i = 0; i < RTP_COUNT; ++i) {
     _clear_value[i] = copy._clear_value[i];
   }
   update_pixel_factor();

+ 10 - 13
panda/src/display/drawableRegion.cxx

@@ -27,8 +27,12 @@ DrawableRegion::
  */
 void DrawableRegion::
 set_clear_active(int n, bool clear_active) {
-  nassertv((n >= 0)&&(n < RTP_COUNT));
-  _clear_active[n] = clear_active;
+  nassertv(n >= 0 && n < RTP_COUNT);
+  if (clear_active) {
+    _clear_mask |= 1 << n;
+  } else {
+    _clear_mask &= ~(1 << n);
+  }
   update_pixel_factor();
 }
 
@@ -37,8 +41,8 @@ set_clear_active(int n, bool clear_active) {
  */
 bool DrawableRegion::
 get_clear_active(int n) const {
-  nassertr((n >= 0)&&(n < RTP_COUNT), false);
-  return _clear_active[n];
+  nassertr(n >= 0 && n < RTP_COUNT, false);
+  return (_clear_mask & (1 << n)) != 0;
 }
 
 /**
@@ -66,9 +70,7 @@ get_clear_value(int n) const {
  */
 void DrawableRegion::
 disable_clears() {
-  for (int i = 0; i < RTP_COUNT; ++i) {
-    _clear_active[i] = false;
-  }
+  _clear_mask = 0;
   update_pixel_factor();
 }
 
@@ -79,12 +81,7 @@ disable_clears() {
  */
 bool DrawableRegion::
 is_any_clear_active() const {
-  for (int i = 0; i < RTP_COUNT; ++i) {
-    if (get_clear_active(i)) {
-      return true;
-    }
-  }
-  return false;
+  return (_clear_mask != 0);
 }
 
 /**

+ 1 - 1
panda/src/display/drawableRegion.h

@@ -109,9 +109,9 @@ protected:
 protected:
   int _screenshot_buffer_type;
   int _draw_buffer_type;
+  int _clear_mask;
 
 private:
-  bool    _clear_active[RTP_COUNT];
   LColor  _clear_value[RTP_COUNT];
 
   PN_stdfloat _pixel_zoom;

+ 7 - 0
panda/src/display/frameBufferProperties.cxx

@@ -480,6 +480,13 @@ get_quality(const FrameBufferProperties &reqs) const {
     quality -= 10000000;
   }
 
+  // Deduct for software-only renderers in absence of a special request.
+  // Cost: 2,000,000
+
+  if (get_force_software() && !reqs.get_force_software()) {
+    quality -= 2000000;
+  }
+
   // Deduct for missing depth, color, alpha, stencil, or accum.  Cost:
   // 1,000,000
 

+ 3 - 0
panda/src/display/graphicsEngine.cxx

@@ -415,6 +415,9 @@ make_output(GraphicsPipe *pipe,
           if (flags & GraphicsPipe::BF_fb_props_optional) {
             display_cat.warning()
               << "FrameBufferProperties available less than requested.\n";
+            display_cat.warning(false)
+              << "  requested: " << fb_prop << "\n"
+              << "  got: " << window->get_fb_properties() << "\n";
             return window;
           }
           display_cat.error()

+ 4 - 1
panda/src/display/graphicsStateGuardian.cxx

@@ -2201,7 +2201,10 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
                       bool force) {
   _munger = munger;
   _data_reader = data_reader;
-  return _data_reader->has_vertex();
+
+  // Always draw if we have a shader, since the shader might use a different
+  // mechanism for fetching vertex data.
+  return _data_reader->has_vertex() || (_target_shader && _target_shader->has_shader());
 }
 
 /**

+ 12 - 0
panda/src/downloader/socketStream.h

@@ -126,6 +126,10 @@ public:
   INLINE ISocketStream(streambuf *buf);
   virtual ~ISocketStream();
 
+#if _MSC_VER >= 1800
+  INLINE ISocketStream(const ISocketStream &copy) = delete;
+#endif
+
 PUBLISHED:
   enum ReadState {
     RS_initial,
@@ -155,6 +159,10 @@ class EXPCL_PANDAEXPRESS OSocketStream : public ostream, public SSWriter {
 public:
   INLINE OSocketStream(streambuf *buf);
 
+#if _MSC_VER >= 1800
+  INLINE OSocketStream(const OSocketStream &copy) = delete;
+#endif
+
 PUBLISHED:
   virtual bool is_closed() = 0;
   virtual void close() = 0;
@@ -170,6 +178,10 @@ class EXPCL_PANDAEXPRESS SocketStream : public iostream, public SSReader, public
 public:
   INLINE SocketStream(streambuf *buf);
 
+#if _MSC_VER >= 1800
+  INLINE SocketStream(const SocketStream &copy) = delete;
+#endif
+
 PUBLISHED:
   virtual bool is_closed() = 0;
   virtual void close() = 0;

+ 5 - 0
panda/src/dxgsg9/config_dxgsg9.cxx

@@ -265,3 +265,8 @@ init_libdxgsg9() {
   PandaSystem *ps = PandaSystem::get_global_ptr();
   ps->add_system("DirectX9");
 }
+
+// Necessary to allow use of dxerr from MSVC 2015
+#if _MSC_VER >= 1900
+int (WINAPIV * __vsnprintf)(char *, size_t, const char*, va_list) = _vsnprintf;
+#endif

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

@@ -4118,19 +4118,18 @@ get_blend_func(ColorBlendAttrib::Operand operand) {
     return D3DBLEND_SRCALPHASAT;
 
   case ColorBlendAttrib::O_incoming1_color:
-    return D3DBLEND_SRCCOLOR2;
+    return (D3DBLEND)16; //D3DBLEND_SRCCOLOR2;
 
   case ColorBlendAttrib::O_one_minus_incoming1_color:
-    return D3DBLEND_INVSRCCOLOR2;
+    return (D3DBLEND)17; //D3DBLEND_INVSRCCOLOR2;
 
   case ColorBlendAttrib::O_incoming1_alpha:
-    // Not supported by DX.
-    return D3DBLEND_SRCCOLOR2;
+    // Not supported by DX9.
+    return (D3DBLEND)18;
 
   case ColorBlendAttrib::O_one_minus_incoming1_alpha:
-    // Not supported by DX.
-    return D3DBLEND_INVSRCCOLOR2;
-
+    // Not supported by DX9.
+    return (D3DBLEND)19;
   }
 
   dxgsg9_cat.error()

+ 11 - 5
panda/src/gles2gsg/gles2gsg.h

@@ -80,8 +80,6 @@ typedef char GLchar;
 #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
 #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT GL_FRAMEBUFFER_INCOMPLETE_FORMATS
 #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT
-#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0
-#define GL_COLOR_ATTACHMENT1_EXT (GL_COLOR_ATTACHMENT0 + 1)
 #define GL_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT
 #define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES
 #define GL_DEPTH_STENCIL_EXT GL_DEPTH_STENCIL_OES
@@ -89,7 +87,6 @@ typedef char GLchar;
 #define GL_DEPTH24_STENCIL8_EXT GL_DEPTH24_STENCIL8_OES
 #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
 #define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES
-#define GL_TEXTURE_3D GL_TEXTURE_3D_OES
 #define GL_MAX_3D_TEXTURE_SIZE GL_MAX_3D_TEXTURE_SIZE_OES
 #define GL_SAMPLER_3D GL_SAMPLER_3D_OES
 #define GL_BGRA GL_BGRA_EXT
@@ -121,13 +118,22 @@ typedef char GLchar;
 #define GL_COMPARE_R_TO_TEXTURE_ARB GL_COMPARE_REF_TO_TEXTURE_EXT
 #define GL_SAMPLER_2D_SHADOW GL_SAMPLER_2D_SHADOW_EXT
 #define GL_MAX_DRAW_BUFFERS GL_MAX_DRAW_BUFFERS_NV
-#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE
-#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE
 #define GL_SRC1_COLOR GL_SRC1_COLOR_EXT
 #define GL_ONE_MINUS_SRC1_COLOR GL_ONE_MINUS_SRC1_COLOR_EXT
 #define GL_SRC1_ALPHA GL_SRC1_ALPHA_EXT
 #define GL_ONE_MINUS_SRC1_ALPHA GL_ONE_MINUS_SRC1_ALPHA_EXT
 
+#define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
+#define GL_DEBUG_TYPE_PERFORMANCE GL_DEBUG_TYPE_PERFORMANCE_KHR
+#define GL_DEBUG_SEVERITY_HIGH GL_DEBUG_SEVERITY_HIGH_KHR
+#define GL_DEBUG_SEVERITY_MEDIUM GL_DEBUG_SEVERITY_MEDIUM_KHR
+#define GL_DEBUG_SEVERITY_LOW GL_DEBUG_SEVERITY_LOW_KHR
+#define GL_DEBUG_SEVERITY_NOTIFICATION GL_DEBUG_SEVERITY_NOTIFICATION_KHR
+#define GL_BUFFER GL_BUFFER_KHR
+#define GL_SHADER GL_SHADER_KHR
+#define GL_PROGRAM GL_PROGRAM_KHR
+#define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
+
 // For GLES 3 compat - need a better solution for this
 #define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x1
 #define GL_ELEMENT_ARRAY_BARRIER_BIT 0x2

Fichier diff supprimé car celui-ci est trop grand
+ 760 - 1050
panda/src/gles2gsg/panda_esgl2ext.h


+ 4 - 5
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -902,7 +902,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
             }
           } else {
             if (_fb_properties.get_color_bits() > 16 * 3) {
-              gl_format = GL_RGBA32F_ARB;
+              gl_format = GL_RGB32F_ARB;
             } else if (_fb_properties.get_color_bits() > 8 * 3) {
               gl_format = GL_RGB16_EXT;
             } else {
@@ -920,11 +920,11 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
             }
           } else {
             if (_fb_properties.get_color_bits() > 16 * 3) {
-              gl_format = GL_RGB32F_ARB;
+              gl_format = GL_RGBA32F_ARB;
             } else if (_fb_properties.get_color_bits() > 8 * 3) {
-              gl_format = GL_RGB16_EXT;
+              gl_format = GL_RGBA16_EXT;
             } else {
-              gl_format = GL_RGB;
+              gl_format = GL_RGBA;
             }
           }
         }
@@ -1090,7 +1090,6 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot,
                                         GL_RENDERBUFFER_EXT, _rbm[slot]);
     }
   } else {
-    Texture *Tex = attach[slot];
     GLuint gl_format = GL_RGBA;
 #ifndef OPENGLES
     switch (slot) {

+ 91 - 43
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -1955,6 +1955,9 @@ reset() {
 #endif
 
 #ifdef OPENGLES_1
+  _supports_framebuffer_multisample = false;
+  _supports_framebuffer_blit = false;
+
   if (has_extension("GL_OES_framebuffer_object")) {
     _supports_framebuffer_object = true;
     _glIsRenderbuffer = (PFNGLISRENDERBUFFEROESPROC)
@@ -2012,9 +2015,76 @@ reset() {
   _glGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
   _glGenerateMipmap = glGenerateMipmap;
 
-#else
-  // TODO: add ARB3.0 version
-  if (has_extension("GL_EXT_framebuffer_object")) {
+  if (is_at_least_gles_version(3, 0)) {
+    _supports_framebuffer_multisample = true;
+    _supports_framebuffer_blit = true;
+
+    _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)
+      get_extension_func("glRenderbufferStorageMultisample");
+    _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFEREXTPROC)
+      get_extension_func("glBlitFramebuffer");
+  } else {
+    if (has_extension("GL_ANGLE_framebuffer_multisample")) {
+      _supports_framebuffer_multisample = true;
+      _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEANGLEPROC)
+        get_extension_func("glRenderbufferStorageMultisampleANGLE");
+    } else {
+      _supports_framebuffer_multisample = false;
+    }
+    if (has_extension("GL_ANGLE_framebuffer_blit")) {
+      _supports_framebuffer_blit = true;
+      _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERANGLEPROC)
+        get_extension_func("glBlitFramebufferANGLE");
+    } else {
+      _supports_framebuffer_blit = false;
+    }
+  }
+#else  // Desktop OpenGL case.
+  if (is_at_least_gl_version(3, 0) || has_extension("GL_ARB_framebuffer_object")) {
+    _supports_framebuffer_object = true;
+    _supports_framebuffer_multisample = true;
+    _supports_framebuffer_blit = true;
+
+    _glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)
+      get_extension_func("glIsRenderbuffer");
+    _glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)
+      get_extension_func("glBindRenderbuffer");
+    _glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)
+      get_extension_func("glDeleteRenderbuffers");
+    _glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)
+      get_extension_func("glGenRenderbuffers");
+    _glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)
+      get_extension_func("glRenderbufferStorage");
+    _glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)
+      get_extension_func("glGetRenderbufferParameteriv");
+    _glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)
+      get_extension_func("glIsFramebuffer");
+    _glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)
+      get_extension_func("glBindFramebuffer");
+    _glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)
+      get_extension_func("glDeleteFramebuffers");
+    _glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)
+      get_extension_func("glGenFramebuffers");
+    _glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)
+      get_extension_func("glCheckFramebufferStatus");
+    _glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)
+      get_extension_func("glFramebufferTexture1D");
+    _glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)
+      get_extension_func("glFramebufferTexture2D");
+    _glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)
+      get_extension_func("glFramebufferTexture3D");
+    _glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)
+      get_extension_func("glFramebufferRenderbuffer");
+    _glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)
+      get_extension_func("glGetFramebufferAttachmentParameteriv");
+    _glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)
+      get_extension_func("glGenerateMipmap");
+    _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
+      get_extension_func("glRenderbufferStorageMultisampleEXT");
+    _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)
+      get_extension_func("glBlitFramebuffer");
+
+  } else if (has_extension("GL_EXT_framebuffer_object")) {
     _supports_framebuffer_object = true;
     _glIsRenderbuffer = (PFNGLISRENDERBUFFEREXTPROC)
       get_extension_func("glIsRenderbufferEXT");
@@ -2051,14 +2121,25 @@ reset() {
     _glGenerateMipmap = (PFNGLGENERATEMIPMAPEXTPROC)
       get_extension_func("glGenerateMipmapEXT");
 
-  } else if (is_at_least_gl_version(3, 0)) {
-    // This case should go away when we support the ARB/3.0 version of FBOs.
-    _supports_framebuffer_object = false;
-    _glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)
-      get_extension_func("glGenerateMipmap");
+    if (has_extension("GL_EXT_framebuffer_multisample")) {
+      _supports_framebuffer_multisample = true;
+      _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)
+        get_extension_func("glRenderbufferStorageMultisampleEXT");
+    } else {
+      _supports_framebuffer_multisample = false;
+    }
+    if (has_extension("GL_EXT_framebuffer_blit")) {
+      _supports_framebuffer_blit = true;
+      _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFEREXTPROC)
+        get_extension_func("glBlitFramebufferEXT");
+    } else {
+      _supports_framebuffer_blit = false;
+    }
 
   } else {
     _supports_framebuffer_object = false;
+    _supports_framebuffer_multisample = false;
+    _supports_framebuffer_blit = false;
     _glGenerateMipmap = NULL;
   }
 #endif
@@ -2087,49 +2168,16 @@ reset() {
   }
 #endif  // !OPENGLES_1
 
-  _supports_framebuffer_multisample = false;
-  if (is_at_least_gles_version(3, 0)) {
-    _supports_framebuffer_multisample = true;
-    _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)
-      get_extension_func("glRenderbufferStorageMultisample");
-
-#ifdef OPENGLES
-  } else if (has_extension("GL_APPLE_framebuffer_multisample")) {
-    _supports_framebuffer_multisample = true;
-    _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEAPPLEPROC)
-      get_extension_func("glRenderbufferStorageMultisampleAPPLE");
-#else
-  } else if (has_extension("GL_EXT_framebuffer_multisample")) {
-    _supports_framebuffer_multisample = true;
-    _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)
-      get_extension_func("glRenderbufferStorageMultisampleEXT");
-#endif
-  }
-
 #ifndef OPENGLES
   _supports_framebuffer_multisample_coverage_nv = false;
-  if (has_extension("GL_NV_framebuffer_multisample_coverage")) {
+  if (_supports_framebuffer_multisample &&
+      has_extension("GL_NV_framebuffer_multisample_coverage")) {
     _supports_framebuffer_multisample_coverage_nv = true;
     _glRenderbufferStorageMultisampleCoverage = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC)
       get_extension_func("glRenderbufferStorageMultisampleCoverageNV");
   }
 #endif
 
-#ifndef OPENGLES_1
-  _supports_framebuffer_blit = false;
-
-  if (is_at_least_gles_version(3, 0)) {
-    _supports_framebuffer_blit = true;
-    _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFEREXTPROC)
-      get_extension_func("glBlitFramebuffer");
-
-  } else if (has_extension("GL_EXT_framebuffer_blit")) {
-    _supports_framebuffer_blit = true;
-    _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFEREXTPROC)
-      get_extension_func("glBlitFramebufferEXT");
-  }
-#endif
-
 #if defined(OPENGLES_1)
   _glDrawBuffers = NULL;
   _max_color_targets = 1;

+ 9 - 8
panda/src/glstuff/glShaderContext_src.cxx

@@ -677,23 +677,24 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
   _glgsg->_glGetActiveUniform(_glsl_program, i, name_buflen, NULL, &param_size, &param_type, name_buffer);
   GLint p = _glgsg->_glGetUniformLocation(_glsl_program, name_buffer);
 
+  if (GLCAT.is_debug()) {
+    GLCAT.debug()
+      << "Active uniform " << name_buffer << " with size " << param_size
+      << " and type 0x" << hex << param_type << dec
+      << " is bound to location " << p << "\n";
+  }
 
   // Some NVidia drivers (361.43 for example) (incorrectly) include "internal"
   // uniforms in the list starting with "_main_" (for example,
   // "_main_0_gp5fp[0]") we need to skip those, because we don't know anything
   // about them
   if (strncmp(name_buffer, "_main_", 6) == 0) {
-    GLCAT.warning() << "Ignoring uniform " << name_buffer << " which may be generated by buggy Nvidia driver.\n";
+    if (GLCAT.is_debug()) {
+      GLCAT.debug() << "Ignoring uniform " << name_buffer << " which may be generated by buggy Nvidia driver.\n";
+    }
     return;
   }
 
-  if (GLCAT.is_debug()) {
-    GLCAT.debug()
-      << "Active uniform " << name_buffer << " with size " << param_size
-      << " and type 0x" << hex << param_type << dec
-      << " is bound to location " << p << "\n";
-  }
-
   if (p < 0) {
     // Special meaning, or it's in a uniform block.  Let it go.
     return;

+ 2 - 1
panda/src/gobj/geomPrimitive.cxx

@@ -2231,7 +2231,8 @@ get_num_primitives() const {
  */
 bool GeomPrimitivePipelineReader::
 check_valid(const GeomVertexDataPipelineReader *data_reader) const {
-  if (get_num_vertices() != 0  &&
+  if (get_num_vertices() != 0 &&
+      data_reader->get_num_arrays() > 0 &&
       get_max_vertex() >= data_reader->get_num_rows()) {
 
 #ifndef NDEBUG

+ 9 - 0
panda/src/gobj/geomVertexFormat.I

@@ -235,6 +235,15 @@ get_morph_delta(size_t n) const {
   return _morphs[n]._delta;
 }
 
+/**
+ * Returns a standard vertex format containing no arrays at all, useful for
+ * pull-style vertex rendering.
+ */
+INLINE const GeomVertexFormat *GeomVertexFormat::
+get_empty() {
+  return get_registry()->_empty;
+}
+
 /**
  * Returns a standard vertex format with just a 3-component vertex position.
  */

+ 2 - 4
panda/src/gobj/geomVertexFormat.cxx

@@ -890,6 +890,8 @@ Registry() {
  */
 void GeomVertexFormat::Registry::
 make_standard_formats() {
+  _empty = register_format(new GeomVertexFormat);
+
   _v3 = register_format(new GeomVertexArrayFormat
                         (InternalName::get_vertex(), 3,
                          NT_stdfloat, C_point));
@@ -1011,10 +1013,6 @@ register_format(GeomVertexFormat *format) {
     new_format = (*fi);
     if (!new_format->is_registered()) {
       new_format->do_register();
-      if (new_format->get_num_arrays() == 0) {
-        gobj_cat.warning()
-          << "Empty GeomVertexFormat registered.\n";
-      }
     }
   }
 

+ 4 - 0
panda/src/gobj/geomVertexFormat.h

@@ -125,6 +125,8 @@ PUBLISHED:
   void write_with_data(ostream &out, int indent_level,
                        const GeomVertexData *data) const;
 
+  INLINE static const GeomVertexFormat *get_empty();
+
   // Some standard vertex formats.  No particular requirement to use one of
   // these, but the DirectX renderers can use these formats directly, whereas
   // any other format will have to be converted first.
@@ -227,6 +229,8 @@ private:
     Formats _formats;
     LightReMutex _lock;
 
+    CPT(GeomVertexFormat) _empty;
+
     CPT(GeomVertexFormat) _v3;
     CPT(GeomVertexFormat) _v3n3;
     CPT(GeomVertexFormat) _v3t2;

+ 5 - 3
panda/src/gobj/shader.cxx

@@ -2370,15 +2370,14 @@ r_preprocess_source(ostream &out, const Filename &fn,
   bool had_include = false;
   int lineno = 0;
   while (getline(*source, line)) {
-    // We always forward the actual line - the GLSL compiler will silently
-    // ignore #pragma lines anyway.
     ++lineno;
-    out << line << "\n";
 
     // Check if this line contains a #pragma.
     char pragma[64];
     if (line.size() < 8 ||
         sscanf(line.c_str(), " # pragma %63s", pragma) != 1) {
+      // Just pass the line through unmodified.
+      out << line << "\n";
 
       // One exception: check for an #endif after an include.  We have to
       // restore the line number in case the include happened under an #if
@@ -2443,8 +2442,11 @@ r_preprocess_source(ostream &out, const Filename &fn,
 
     } else if (strcmp(pragma, "optionNV") == 0) {
       // This is processed by NVIDIA drivers.  Don't touch it.
+      out << line << "\n";
 
     } else {
+      // Forward it, the driver will ignore it if it doesn't know it.
+      out << line << "\n";
       shader_cat.warning()
         << "Ignoring unknown pragma directive \"" << pragma << "\" at line "
         << lineno << " of file " << fn << ":\n  " << line << "\n";

+ 174 - 78
panda/src/gobj/texture.cxx

@@ -830,8 +830,11 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
         } else if (format.at(s) == 'R') {
           component = 2;
         } else if (format.at(s) == 'A') {
-          nassertv(cdata->_num_components != 3);
-          component = cdata->_num_components - 1;
+          if (cdata->_num_components != 3) {
+            component = cdata->_num_components - 1;
+          } else {
+            // Ignore.
+          }
         } else if (format.at(s) == '0') {
           // Ignore.
         } else if (format.at(s) == '1') {
@@ -859,8 +862,11 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
       } else if (format.at(s) == 'R') {
         component = 2;
       } else if (format.at(s) == 'A') {
-        nassertv(cdata->_num_components != 3);
-        component = cdata->_num_components - 1;
+        if (cdata->_num_components != 3) {
+          component = cdata->_num_components - 1;
+        } else {
+          // Ignore.
+        }
       } else if (format.at(s) == '0') {
         // Ignore.
       } else if (format.at(s) == '1') {
@@ -6088,18 +6094,23 @@ do_get_uncompressed_ram_image(CData *cdata) {
  * Rather than just returning a pointer to the data, like
  * get_uncompressed_ram_image, this function first processes the data and
  * reorders the components using the specified format string, and places these
- * into a new char array.  The 'format' argument should specify in which order
- * the components of the texture must be.  For example, valid format strings
- * are "RGBA", "GA", "ABRG" or "AAA". A component can also be written as "0"
- * or "1", which means an empty/black or a full/white channel, respectively.
+ * into a new char array.
+ *
+ * The 'format' argument should specify in which order the components of the
+ * texture must be.  For example, valid format strings are "RGBA", "GA",
+ * "ABRG" or "AAA".  A component can also be written as "0" or "1", which
+ * means an empty/black or a full/white channel, respectively.
+ *
  * This function is particularly useful to copy an image in-memory to a
  * different library (for example, PIL or wxWidgets) that require a different
  * component order than Panda's internal format, BGRA. Note, however, that
  * this conversion can still be too slow if you want to do it every frame, and
- * should thus be avoided for that purpose.  The only requirement for the
- * reordering is that an uncompressed image must be available.  If the RAM
- * image is compressed, it will attempt to re-load the texture from disk, if
- * it doesn't find an uncompressed image there, it will return NULL.
+ * should thus be avoided for that purpose.
+ *
+ * The only requirement for the reordering is that an uncompressed image must
+ * be available.  If the RAM image is compressed, it will attempt to re-load
+ * the texture from disk, if it doesn't find an uncompressed image there, it
+ * will return NULL.
  */
 CPTA_uchar Texture::
 get_ram_image_as(const string &requested_format) {
@@ -6125,92 +6136,177 @@ get_ram_image_as(const string &requested_format) {
     return CPTA_uchar(data);
   }
 
+  // Check if we have an alpha channel, and remember which channel we use.
+  int alpha = -1;
+  if (Texture::has_alpha(cdata->_format)) {
+    alpha = cdata->_num_components - 1;
+  }
+
+  // Validate the format beforehand.
+  for (size_t i = 0; i < format.size(); ++i) {
+    if (format[i] != 'B' && format[i] != 'G' && format[i] != 'R' &&
+        format[i] != 'A' && format[i] != '0' && format[i] != '1') {
+      gobj_cat.error() << "Unexpected component character '"
+        << format[i] << "', expected one of RGBA01!\n";
+      return CPTA_uchar(get_class_type());
+    }
+  }
+
   // Create a new empty array that can hold our image.
   PTA_uchar newdata = PTA_uchar::empty_array(imgsize * format.size() * cdata->_component_width, get_class_type());
 
   // These ifs are for optimization of commonly used image types.
-  if (format == "RGBA" && cdata->_num_components == 4 && cdata->_component_width == 1) {
-    imgsize *= 4;
-    for (int p = 0; p < imgsize; p += 4) {
-      newdata[p    ] = data[p + 2];
-      newdata[p + 1] = data[p + 1];
-      newdata[p + 2] = data[p    ];
-      newdata[p + 3] = data[p + 3];
+  if (cdata->_component_width == 1) {
+    if (format == "RGBA" && cdata->_num_components == 4) {
+      const PN_uint32 *src = (const PN_uint32 *)data.p();
+      PN_uint32 *dst = (PN_uint32 *)newdata.p();
+
+      for (int p = 0; p < imgsize; ++p) {
+        PN_uint32 v = *src++;
+        *dst++ = ((v & 0xff00ff00u)) |
+                 ((v & 0x00ff0000u) >> 16) |
+                 ((v & 0x000000ffu) << 16);
+      }
+      return newdata;
+    }
+    if (format == "RGB" && cdata->_num_components == 4) {
+      const PN_uint32 *src = (const PN_uint32 *)data.p();
+      PN_uint32 *dst = (PN_uint32 *)newdata.p();
+
+      // Convert blocks of 4 pixels at a time, so that we can treat both the
+      // source and destination as 32-bit integers.
+      int blocks = imgsize >> 2;
+      for (int i = 0; i < blocks; ++i) {
+        PN_uint32 v0 = *src++;
+        PN_uint32 v1 = *src++;
+        PN_uint32 v2 = *src++;
+        PN_uint32 v3 = *src++;
+        *dst++ = ((v0 & 0x00ff0000u) >> 16) |
+                 ((v0 & 0x0000ff00u)) |
+                 ((v0 & 0x000000ffu) << 16) |
+                 ((v1 & 0x00ff0000u) << 8);
+        *dst++ = ((v1 & 0x0000ff00u) >> 8) |
+                 ((v1 & 0x000000ffu) << 8) |
+                 ((v2 & 0x00ff0000u)) |
+                 ((v2 & 0x0000ff00u) << 16);
+        *dst++ = ((v2 & 0x000000ffu)) |
+                 ((v3 & 0x00ff0000u) >> 8) |
+                 ((v3 & 0x0000ff00u) << 8) |
+                 ((v3 & 0x000000ffu) << 24);
+      }
+
+      // If the image size wasn't a multiple of 4, we may have a handful of
+      // pixels left over.  Convert those the slower way.
+      PN_uint8 *tail = (PN_uint8 *)dst;
+      for (int i = (imgsize & ~0x3); i < imgsize; ++i) {
+        PN_uint32 v = *src++;
+        *tail++ = (v & 0x00ff0000u) >> 16;
+        *tail++ = (v & 0x0000ff00u) >> 8;
+        *tail++ = (v & 0x000000ffu);
+      }
+      return newdata;
+    }
+    if (format == "BGR" && cdata->_num_components == 4) {
+      const PN_uint32 *src = (const PN_uint32 *)data.p();
+      PN_uint32 *dst = (PN_uint32 *)newdata.p();
+
+      // Convert blocks of 4 pixels at a time, so that we can treat both the
+      // source and destination as 32-bit integers.
+      int blocks = imgsize >> 2;
+      for (int i = 0; i < blocks; ++i) {
+        PN_uint32 v0 = *src++;
+        PN_uint32 v1 = *src++;
+        PN_uint32 v2 = *src++;
+        PN_uint32 v3 = *src++;
+        *dst++ = (v0 & 0x00ffffffu) | ((v1 & 0x000000ffu) << 24);
+        *dst++ = ((v1 & 0x00ffff00u) >> 8) |  ((v2 & 0x0000ffffu) << 16);
+        *dst++ = ((v2 & 0x00ff0000u) >> 16) | ((v3 & 0x00ffffffu) << 8);
+      }
+
+      // If the image size wasn't a multiple of 4, we may have a handful of
+      // pixels left over.  Convert those the slower way.
+      PN_uint8 *tail = (PN_uint8 *)dst;
+      for (int i = (imgsize & ~0x3); i < imgsize; ++i) {
+        PN_uint32 v = *src++;
+        *tail++ = (v & 0x000000ffu);
+        *tail++ = (v & 0x0000ff00u) >> 8;
+        *tail++ = (v & 0x00ff0000u) >> 16;
+      }
+      return newdata;
     }
-    return newdata;
-  }
-  if (format == "RGB" && cdata->_num_components == 3 && cdata->_component_width == 1) {
-    imgsize *= 3;
-    for (int p = 0; p < imgsize; p += 3) {
-      newdata[p    ] = data[p + 2];
-      newdata[p + 1] = data[p + 1];
-      newdata[p + 2] = data[p    ];
+    const PN_uint8 *src = (const PN_uint8 *)data.p();
+    PN_uint8 *dst = (PN_uint8 *)newdata.p();
+
+    if (format == "RGB" && cdata->_num_components == 3) {
+      for (int i = 0; i < imgsize; ++i) {
+        *dst++ = src[2];
+        *dst++ = src[1];
+        *dst++ = src[0];
+        src += 3;
+      }
+      return newdata;
     }
-    return newdata;
-  }
-  if (format == "A" && cdata->_component_width == 1 && cdata->_num_components != 3) {
-    // We can generally rely on alpha to be the last component.
-    int component = cdata->_num_components - 1;
-    for (int p = 0; p < imgsize; ++p) {
-      newdata[p] = data[component];
+    if (format == "A" && cdata->_num_components != 3) {
+      // We can generally rely on alpha to be the last component.
+      for (int p = 0; p < imgsize; ++p) {
+        dst[p] = src[alpha];
+        src += cdata->_num_components;
+      }
+      return newdata;
     }
-    return newdata;
-  }
-  if (cdata->_component_width == 1) {
+    // Fallback case for other 8-bit-per-channel formats.
     for (int p = 0; p < imgsize; ++p) {
-      for (uchar s = 0; s < format.size(); ++s) {
-        signed char component = -1;
-        if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
-          component = 0;
-        } else if (format.at(s) == 'G') {
-          component = 1;
-        } else if (format.at(s) == 'R') {
-          component = 2;
-        } else if (format.at(s) == 'A') {
-          nassertr(cdata->_num_components != 3, CPTA_uchar(get_class_type()));
-          component = cdata->_num_components - 1;
-        } else if (format.at(s) == '0') {
-          newdata[p * format.size() + s] = 0x00;
-        } else if (format.at(s) == '1') {
-          newdata[p * format.size() + s] = 0xff;
+      for (size_t i = 0; i < format.size(); ++i) {
+        if (format[i] == 'B' || (cdata->_num_components <= 2 && format[i] != 'A')) {
+          *dst++ = src[0];
+        } else if (format[i] == 'G') {
+          *dst++ = src[1];
+        } else if (format[i] == 'R') {
+          *dst++ = src[2];
+        } else if (format[i] == 'A') {
+          if (alpha >= 0) {
+            *dst++ = src[alpha];
+          } else {
+            *dst++ = 0xff;
+          }
+        } else if (format[i] == '1') {
+          *dst++ = 0xff;
         } else {
-          gobj_cat.error() << "Unexpected component character '"
-            << format.at(s) << "', expected one of RGBA!\n";
-          return CPTA_uchar(get_class_type());
-        }
-        if (component >= 0) {
-          newdata[p * format.size() + s] = data[p * cdata->_num_components + component];
+          *dst++ = 0x00;
         }
       }
+      src += cdata->_num_components;
     }
     return newdata;
   }
+
+  // The slow and general case.
   for (int p = 0; p < imgsize; ++p) {
-    for (uchar s = 0; s < format.size(); ++s) {
-      signed char component = -1;
-      if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
+    for (size_t i = 0; i < format.size(); ++i) {
+      int component = 0;
+      if (format[i] == 'B' || (cdata->_num_components <= 2 && format[i] != 'A')) {
         component = 0;
-      } else if (format.at(s) == 'G') {
+      } else if (format[i] == 'G') {
         component = 1;
-      } else if (format.at(s) == 'R') {
+      } else if (format[i] == 'R') {
         component = 2;
-      } else if (format.at(s) == 'A') {
-        nassertr(cdata->_num_components != 3, CPTA_uchar(get_class_type()));
-        component = cdata->_num_components - 1;
-      } else if (format.at(s) == '0') {
-        memset((void*)(newdata + (p * format.size() + s) * cdata->_component_width),  0, cdata->_component_width);
-      } else if (format.at(s) == '1') {
-        memset((void*)(newdata + (p * format.size() + s) * cdata->_component_width), -1, cdata->_component_width);
+      } else if (format[i] == 'A') {
+        if (alpha >= 0) {
+          component = alpha;
+        } else {
+          memset((void*)(newdata + (p * format.size() + i) * cdata->_component_width), -1, cdata->_component_width);
+          continue;
+        }
+      } else if (format[i] == '1') {
+        memset((void*)(newdata + (p * format.size() + i) * cdata->_component_width), -1, cdata->_component_width);
+        continue;
       } else {
-        gobj_cat.error() << "Unexpected component character '"
-          << format.at(s) << "', expected one of RGBA!\n";
-        return CPTA_uchar(get_class_type());
-      }
-      if (component >= 0) {
-        memcpy((void*)(newdata + (p * format.size() + s) * cdata->_component_width),
-               (void*)(data + (p * cdata->_num_components + component) * cdata->_component_width),
-               cdata->_component_width);
+        memset((void*)(newdata + (p * format.size() + i) * cdata->_component_width),  0, cdata->_component_width);
+        continue;
       }
+      memcpy((void*)(newdata + (p * format.size() + i) * cdata->_component_width),
+             (void*)(data + (p * cdata->_num_components + component) * cdata->_component_width),
+             cdata->_component_width);
     }
   }
   return newdata;

+ 2 - 0
panda/src/grutil/config_grutil.cxx

@@ -23,6 +23,7 @@
 #include "nodeVertexTransform.h"
 #include "rigidBodyCombiner.h"
 #include "pipeOcclusionCullTraverser.h"
+#include "shaderTerrainMesh.h"
 
 #include "dconfig.h"
 
@@ -123,6 +124,7 @@ init_libgrutil() {
   RigidBodyCombiner::init_type();
   PipeOcclusionCullTraverser::init_type();
   SceneGraphAnalyzerMeter::init_type();
+  ShaderTerrainMesh::init_type();
 
 #ifdef HAVE_AUDIO
   MovieTexture::init_type();

+ 1 - 0
panda/src/grutil/p3grutil_composite1.cxx

@@ -1,6 +1,7 @@
 #include "cardMaker.cxx"
 #include "heightfieldTesselator.cxx"
 #include "geoMipTerrain.cxx"
+#include "shaderTerrainMesh.cxx"
 #include "config_grutil.cxx"
 #include "lineSegs.cxx"
 #include "fisheyeMaker.cxx"

+ 191 - 0
panda/src/grutil/shaderTerrainMesh.I

@@ -0,0 +1,191 @@
+/**
+ * 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 shaderTerrainMesh.I
+ * @author tobspr
+ * @date 2016-02-16
+ */
+
+/**
+ * @brief Sets the path to the heightfield
+ * @details This sets the path to the terrain heightfield. 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
+ */
+INLINE void ShaderTerrainMesh::set_heightfield_filename(const Filename& filename) {
+  _heightfield_source = filename;
+}
+
+/**
+ * @brief Returns the heightfield path
+ * @details This returns the path of the terrain heightfield, previously set with
+ *   set_heightfield()
+ *
+ * @return Path to the heightfield
+ */
+INLINE const Filename& ShaderTerrainMesh::get_heightfield_filename() const {
+  return _heightfield_source;
+}
+
+/**
+ * @brief Sets the chunk size
+ * @details This sets the chunk size of the terrain. A chunk is basically the
+ *   smallest unit in LOD. If the chunk size is too small, the terrain will
+ *   perform bad, since there will be way too many chunks. If the chunk size
+ *   is too big, you will not get proper LOD, and might also get bad performance.
+ *
+ *   For terrains of the size 4096x4096 or 8192x8192, a chunk size of 32 seems
+ *   to produce good results. For smaller resolutions, you should try out a
+ *   size of 16 or even 8 for very small terrains.
+ *
+ *   The amount of chunks generated for the last level equals to
+ *   (heightfield_size / chunk_size) ** 2. The chunk size has to be a power
+ *   of two.
+ *
+ * @param chunk_size Size of the chunks, has to be a power of two
+ */
+INLINE void ShaderTerrainMesh::set_chunk_size(size_t chunk_size) {
+  _chunk_size = chunk_size;
+}
+
+/**
+ * @brief Returns the chunk size
+ * @details This returns the chunk size, previously set with set_chunk_size()
+ * @return Chunk size
+ */
+INLINE size_t ShaderTerrainMesh::get_chunk_size() const {
+  return _chunk_size;
+}
+
+/**
+ * @brief Sets whether to generate patches
+ * @details If this option is set to true, GeomPatches will be used instead of
+ *   GeomTriangles. This is required when the terrain is used with tesselation
+ *   shaders, since patches are required for tesselation, whereas triangles
+ *   are required for regular rendering.
+ *
+ *   If this option is set to true while not using a tesselation shader, the
+ *   terrain will not get rendered, or even produce errors. The same applies
+ *   when this is option is not set, but the terrain is used with tesselation
+ *   shaders.
+ *
+ * @param generate_patches [description]
+ */
+INLINE void ShaderTerrainMesh::set_generate_patches(bool generate_patches) {
+  _generate_patches = generate_patches;
+}
+
+/**
+ * @brief Returns whether to generate patches
+ * @details This returns whether patches are generated, previously set with
+ *   set_generate_patches()
+ *
+ * @return Whether to generate patches
+ */
+INLINE bool ShaderTerrainMesh::get_generate_patches() const {
+  return _generate_patches;
+}
+
+
+/**
+ * @brief Sets the desired triangle width
+ * @details This sets the desired width a triangle should have in pixels.
+ *   A value of 10.0 for example will make the terrain tesselate everything
+ *   in a way that each triangle edge roughly is 10 pixels wide.
+ *   Of course this will not always accurately match, however you can use this
+ *   setting to control the LOD algorithm of the terrain.
+ *
+ * @param target_triangle_width Desired triangle width in pixels
+ */
+INLINE void ShaderTerrainMesh::set_target_triangle_width(PN_stdfloat target_triangle_width) {
+  _target_triangle_width = target_triangle_width;
+}
+
+/**
+ * @brief Returns the target triangle width
+ * @details This returns the target triangle width, previously set with
+ *   ShaderTerrainMesh::set_target_triangle_width()
+ *
+ * @return Target triangle width
+ */
+INLINE PN_stdfloat ShaderTerrainMesh::get_target_triangle_width() const {
+  return _target_triangle_width;
+}
+
+
+/**
+ * @brief Sets whether to enable terrain updates
+ * @details This flag controls whether the terrain should be updated. If this value
+ *   is set to false, no updating of the terrain will happen. This can be useful
+ *   to debug the culling algorithm used by the terrain.
+ *
+ * @param update_enabled Whether to update the terrain
+ */
+INLINE void ShaderTerrainMesh::set_update_enabled(bool update_enabled) {
+  _update_enabled = update_enabled;
+}
+
+/**
+ * @brief Returns whether the terrain is getting updated
+ * @details This returns whether the terrain is getting updates, previously set with
+ *   set_update_enabled()
+ *
+ * @return Whether to update the terrain
+ */
+INLINE bool ShaderTerrainMesh::get_update_enabled() const {
+  return _update_enabled;
+}
+
+/**
+ * @brief Returns a handle to the heightfield texture
+ * @details This returns a handle to the internally used heightfield texture. This
+ *   can be used to set the heightfield as a shader input.
+ *
+ * @return Handle to the heightfield texture
+ */
+INLINE Texture* ShaderTerrainMesh::get_heightfield_tex() const {
+  return _heightfield_tex;
+}
+
+/**
+ * @brief Clears all children
+ * @details This clears all children on the chunk and sets them to NULL. This will
+ *   effectively free all memory consumed by this chunk and its children.
+ */
+INLINE void ShaderTerrainMesh::Chunk::clear_children() {
+  for (size_t i = 0; i < 4; ++i) {
+    delete children[i];
+    children[i] = NULL;
+  }
+}
+
+/**
+ * @brief Chunk constructor
+ * @details This constructs a new chunk, and sets all children to NULL.
+ */
+INLINE ShaderTerrainMesh::Chunk::Chunk() {
+  for (size_t i = 0; i < 4; ++i)
+    children[i] = NULL;
+}
+
+/**
+ * @brief Chunk destructor
+ * @details This destructs the chunk, freeing all used resources
+ */
+INLINE ShaderTerrainMesh::Chunk::~Chunk() {
+  clear_children();
+}
+
+/**
+ * @see ShaderTerrainMesh::uv_to_world(LTexCoord)
+ */
+INLINE LPoint3 ShaderTerrainMesh::uv_to_world(PN_stdfloat u, PN_stdfloat v) const {
+  return uv_to_world(LTexCoord(u, v));
+}

+ 715 - 0
panda/src/grutil/shaderTerrainMesh.cxx

@@ -0,0 +1,715 @@
+/**
+ * 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 shaderTerrainMesh.cxx
+ * @author tobspr
+ * @date 2016-02-16
+ */
+
+
+#include "shaderTerrainMesh.h"
+#include "geom.h"
+#include "geomVertexFormat.h"
+#include "geomVertexData.h"
+#include "geomVertexWriter.h"
+#include "geomNode.h"
+#include "geomTriangles.h"
+#include "geomPatches.h"
+#include "omniBoundingVolume.h"
+#include "cullableObject.h"
+#include "cullTraverser.h"
+#include "cullHandler.h"
+#include "cullTraverserData.h"
+#include "clockObject.h"
+#include "shaderAttrib.h"
+#include "renderAttrib.h"
+#include "shaderInput.h"
+#include "boundingBox.h"
+#include "samplerState.h"
+#include "config_grutil.h"
+#include "typeHandle.h"
+
+ConfigVariableBool stm_use_hexagonal_layout
+("stm-use-hexagonal-layout", true,
+ 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."));
+
+ConfigVariableInt stm_max_chunk_count
+("stm-max-chunk-count", 2048,
+ PRC_DESC("Controls the maximum amount of chunks the Terrain can display. If you use "
+          "a high LOD, you might have to increment this value. The lower this value is "
+          "the less data has to be transferred to the GPU."));
+
+ConfigVariableInt stm_max_views
+("stm-max-views", 8,
+ PRC_DESC("Controls the maximum amount of different views the Terrain can be rendered "
+          "with. Each camera rendering the terrain corresponds to a view. Lowering this "
+          "value will reduce the data that has to be transferred to the GPU."));
+
+PStatCollector ShaderTerrainMesh::_basic_collector("Cull:ShaderTerrainMesh:Setup");
+PStatCollector ShaderTerrainMesh::_lod_collector("Cull:ShaderTerrainMesh:CollectLOD");
+
+NotifyCategoryDef(shader_terrain, "");
+
+TypeHandle ShaderTerrainMesh::_type_handle;
+
+/**
+ * @brief Helper function to check for a power of two
+ * @details This method checks for a power of two by using bitmasks
+ *
+ * @param x Number to check
+ * @return true if x is a power of two, false otherwise
+ */
+int check_power_of_two(size_t x)
+{
+  return ((x != 0) && ((x & (~x + 1)) == x));
+}
+
+/**
+ * @brief Constructs a new Terrain Mesh
+ * @details This constructs a new terrain mesh. By default, no transform is set
+ *   on the mesh, causing it to range over the unit box from (0, 0, 0) to
+ *   (1, 1, 1). Usually you want to set a custom transform with NodePath::set_scale()
+ */
+ShaderTerrainMesh::ShaderTerrainMesh() :
+  PandaNode("ShaderTerrainMesh"),
+  _size(0),
+  _chunk_size(32),
+  _heightfield_source(""),
+  _generate_patches(false),
+  _data_texture(NULL),
+  _chunk_geom(NULL),
+  _current_view_index(0),
+  _last_frame_count(-1),
+  _target_triangle_width(10.0f),
+  _update_enabled(true),
+  _heightfield_tex(NULL)
+{
+  set_final(true);
+  set_bounds(new OmniBoundingVolume());
+}
+
+/**
+ * @brief Generates the terrain mesh
+ * @details This generates the terrain mesh, initializing all chunks of the
+ *   internal used quadtree. At this point, a heightfield and a chunk size should
+ *   have been set, otherwise an error is thrown.
+ *
+ *   If anything goes wrong, like a missing heightfield, then an error is printed
+ *   and false is returned.
+ *
+ * @return true if the terrain was initialized, false if an error occured
+ */
+bool ShaderTerrainMesh::generate() {
+  if (!do_load_heightfield())
+    return false;
+
+  if (_chunk_size < 8 || !check_power_of_two(_chunk_size)) {
+    shader_terrain_cat.error() << "Invalid chunk size! Has to be >= 8 and a power of two!" << endl;
+    return false;
+  }
+
+  if (_chunk_size > _size / 4) {
+    shader_terrain_cat.error() << "Chunk size too close or greater than the actual terrain size!" << endl;
+    return false;
+  }
+
+  do_create_chunks();
+  do_compute_bounds(&_base_chunk);
+  do_create_chunk_geom();
+  do_init_data_texture();
+  do_convert_heightfield();
+
+  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)
+ */
+void ShaderTerrainMesh::do_convert_heightfield() {
+  _heightfield_tex = new Texture();
+  _heightfield_tex->load(_heightfield);
+  _heightfield_tex->set_keep_ram_image(true);
+
+  if (_heightfield.get_maxval() != 65535) {
+    shader_terrain_cat.warning() << "Using non 16-bit heightfield!" << endl;
+  } else {
+    _heightfield_tex->set_format(Texture::F_r16);
+  }
+  _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,
+ *   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
+ */
+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()) {
+    shader_terrain_cat.error() << "Only square heightfields are supported!";
+    return false;
+  }
+
+  _size = _heightfield.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;
+    return false;
+  }
+
+  return true;
+}
+
+/**
+ * @brief Internal method to init the terrain data texture
+ * @details This method creates the data texture, used to store all chunk data.
+ *   The data texture is set as a shader input later on, and stores the position
+ *   and scale of each chunk. Every row in the data texture denotes a view on
+ *   the terrain.
+ */
+void ShaderTerrainMesh::do_init_data_texture() {
+  _data_texture = new Texture("TerrainDataTexture");
+  _data_texture->setup_2d_texture(stm_max_chunk_count, stm_max_views, Texture::T_float, Texture::F_rgba32);
+  _data_texture->set_clear_color(LVector4(0));
+  _data_texture->clear_image();
+}
+
+/**
+ * @brief Internal method to init the quadtree
+ * @details This method creates the base chunk and then inits all chunks recursively
+ *   by using ShaderTerrainMesh::do_init_chunk().
+ */
+void ShaderTerrainMesh::do_create_chunks() {
+
+  // Release any previously stored children
+  _base_chunk.clear_children();
+
+  // Create the base chunk
+  _base_chunk.depth = 0;
+  _base_chunk.x = 0;
+  _base_chunk.y = 0;
+  _base_chunk.size = _size;
+  _base_chunk.edges.set(0, 0, 0, 0);
+  _base_chunk.avg_height = 0.5;
+  _base_chunk.min_height = 0.0;
+  _base_chunk.max_height = 1.0;
+  _base_chunk.last_clod = 0.0;
+  do_init_chunk(&_base_chunk);
+}
+
+/**
+ * @brief Internal method to recursively init the quadtree
+ * @details This method inits the quadtree. Starting from a given node, it
+ *   first examines if that node should be subdivided.
+ *
+ *   If the node should be subdivided, four children are created and this method
+ *   is called on the children again. If the node is a leaf, all children are
+ *   set to NULL and nothing else happens.
+ *
+ *   The chunk parameter may not be zero or undefined behaviour occurs.
+ *
+ * @param chunk The parent chunk
+ */
+void ShaderTerrainMesh::do_init_chunk(Chunk* chunk) {
+  if (chunk->size > _chunk_size) {
+
+    // Compute children chunk size
+    size_t child_chunk_size = chunk->size / 2;
+
+    // Subdivide chunk into 4 children
+    for (size_t y = 0; y < 2; ++y) {
+      for (size_t x = 0; x < 2; ++x) {
+        Chunk* child = new Chunk();
+        child->size = child_chunk_size;
+        child->depth = chunk->depth + 1;
+        child->x = chunk->x + x * child_chunk_size;
+        child->y = chunk->y + y * child_chunk_size;
+        do_init_chunk(child);
+        chunk->children[x + 2*y] = child;
+      }
+    }
+  } else {
+    // Final chunk, initialize all children to zero
+    for (size_t i = 0; i < 4; ++i) {
+      chunk->children[i] = NULL;
+    }
+  }
+}
+
+/**
+ * @brief Recursively computes the bounds for a given chunk
+ * @details This method takes a parent chunk, and computes the bounds recursively,
+ *   depending on whether the chunk is a leaf or a node.
+ *
+ *   If the chunk is a leaf, then the average, min and max values for that chunk
+ *   are computed by iterating over the heightfield region of that chunk.
+ *
+ *   If the chunk is a node, this method is called recursively on all children
+ *   first, and after that, the average, min and max values for that chunk
+ *   are computed by merging those values of the children.
+ *
+ *   If chunk is NULL, undefined behaviour occurs.
+ *
+ * @param chunk The parent chunk
+ */
+void ShaderTerrainMesh::do_compute_bounds(Chunk* chunk) {
+
+  // Final chunk (Leaf)
+  if (chunk->size == _chunk_size) {
+
+    // Get a pointer to the PNMImage data, this is faster than using get_xel()
+    // for all pixels, since get_xel() also includes bounds checks and so on.
+    xel* data = _heightfield.get_array();
+
+    // Pixel getter function. Note that we have to flip the Y-component, since
+    // panda itself also flips it
+    // auto get_xel = [&](size_t x, size_t y){ return data[x + (_size - 1 - y) * _size].b / (PN_stdfloat)PGM_MAXMAXVAL; };
+    #define get_xel(x, y) (data[(x) + (_size - 1 - (y)) * _size].b / (PN_stdfloat)PGM_MAXMAXVAL)
+
+    // Iterate over all pixels
+    PN_stdfloat avg_height = 0.0, min_height = 1.0, max_height = 0.0;
+    for (size_t x = 0; x < _chunk_size; ++x) {
+      for (size_t y = 0; y < _chunk_size; ++y) {
+
+        // Access data directly, to improve performance
+        PN_stdfloat height = get_xel(chunk->x + x, chunk->y + y);
+        avg_height += height;
+        min_height = min(min_height, height);
+        max_height = max(max_height, height);
+      }
+    }
+
+    // Normalize average height
+    avg_height /= _chunk_size * _chunk_size;
+
+    // Store values
+    chunk->min_height = min_height;
+    chunk->max_height = max_height;
+    chunk->avg_height = avg_height;
+
+    // Get edges in the order (0, 0) (1, 0) (0, 1) (1, 1)
+    for (size_t y = 0; y < 2; ++y) {
+      for (size_t x = 0; x < 2; ++x) {
+        chunk->edges.set_cell(x + 2 * y, get_xel(
+            chunk->x + x * (_chunk_size - 1),
+            chunk->y + y * (_chunk_size - 1)
+          ));
+      }
+    }
+
+    #undef get_xel
+
+  } else {
+
+    // Reset heights
+    chunk->avg_height = 0.0;
+    chunk->min_height = 1.0;
+    chunk->max_height = 0.0;
+
+    // Perform bounds computation for every children and merge the children values
+    for (size_t i = 0; i < 4; ++i) {
+      do_compute_bounds(chunk->children[i]);
+      chunk->avg_height += chunk->children[i]->avg_height / 4.0;
+      chunk->min_height = min(chunk->min_height, chunk->children[i]->min_height);
+      chunk->max_height = max(chunk->max_height, chunk->children[i]->max_height);
+    }
+
+    // Also take the edge points from the children
+    chunk->edges.set_x(chunk->children[0]->edges.get_x());
+    chunk->edges.set_y(chunk->children[1]->edges.get_y());
+    chunk->edges.set_z(chunk->children[2]->edges.get_z());
+    chunk->edges.set_w(chunk->children[3]->edges.get_w());
+  }
+}
+
+/**
+ * @brief Internal method to create the chunk geom
+ * @details This method generates the internal used base chunk. The base chunk geom
+ *   is used to render the actual terrain, and will get instanced for every chunk.
+ *
+ *   The chunk has a size of (size+3) * (size+3), since additional triangles are
+ *   inserted at the borders to prevent holes between chunks of a different LOD.
+ *
+ *   If the generate patches option is set, patches will be generated instead
+ *   of triangles, which allows the terrain to use a tesselation shader.
+ */
+void ShaderTerrainMesh::do_create_chunk_geom() {
+
+  // Convert chunk size to an integer, because we operate on integers and get
+  // signed/unsigned mismatches otherwise
+  int size = (int)_chunk_size;
+
+  // Create vertex data
+  PT(GeomVertexData) gvd = new GeomVertexData("vertices", GeomVertexFormat::get_v3(), Geom::UH_static);
+  gvd->reserve_num_rows( (size + 3) * (size + 3) );
+  GeomVertexWriter vertex_writer(gvd, "vertex");
+
+  // Create primitive
+  PT(GeomPrimitive) triangles = NULL;
+  if (_generate_patches) {
+    triangles = new GeomPatches(3, Geom::UH_static);
+  } else {
+    triangles = new GeomTriangles(Geom::UH_static);
+  }
+
+  // Insert chunk vertices
+  for (int y = -1; y <= size + 1; ++y) {
+    for (int x = -1; x <= size + 1; ++x) {
+      LVector3 vtx_pos(x / (PN_stdfloat)size, y / (PN_stdfloat)size, 0.0f);
+      // 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())));
+      }
+      vertex_writer.add_data3f(vtx_pos);
+    }
+  }
+
+  // Its important to use int and not size_t here, since we do store negative values
+  // auto get_point_index = [&size](int x, int y){ return (x + 1) + (size + 3) * (y + 1); };
+  #define get_point_index(x, y) (((x) + 1) + (size + 3) * ((y) + 1))
+
+  // Create triangles
+  for (int y = -1; y <= size; ++y) {
+    for (int x = -1; x <= size; ++x) {
+      // Get point indices of the quad vertices
+      int tl = get_point_index(x, y);
+      int tr = get_point_index(x + 1, y);
+      int bl = get_point_index(x, y + 1);
+      int br = get_point_index(x + 1, y + 1);
+
+      // Vary triangle scheme on each uneven quad
+      if (stm_use_hexagonal_layout && (x + y) % 2 == 0 ) {
+        triangles->add_vertices(tl, tr, br);
+        triangles->add_vertices(tl, br, bl);
+      } else {
+        triangles->add_vertices(tl, tr, bl);
+        triangles->add_vertices(bl, tr, br);
+      }
+    }
+  }
+
+  #undef get_point_index
+
+  // Construct geom
+  PT(Geom) geom = new Geom(gvd);
+  geom->add_primitive(triangles);
+
+  // Do not set any bounds, we do culling ourself
+  geom->clear_bounds();
+  geom->set_bounds(new OmniBoundingVolume());
+  _chunk_geom = geom;
+}
+
+/**
+ * @copydoc PandaNode::is_renderable()
+ */
+bool ShaderTerrainMesh::is_renderable() const {
+  return true;
+}
+
+/**
+ * @copydoc PandaNode::is_renderable()
+ */
+bool ShaderTerrainMesh::safe_to_flatten() const {
+  return false;
+}
+
+/**
+ * @copydoc PandaNode::safe_to_combine()
+ */
+bool ShaderTerrainMesh::safe_to_combine() const {
+  return false;
+}
+
+/**
+ * @copydoc PandaNode::add_for_draw()
+ */
+void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &data) {
+
+  // Make sure the terrain was properly initialized, and the geom was created
+  // successfully
+  nassertv(_data_texture != NULL);
+  nassertv(_chunk_geom != NULL);
+
+  _basic_collector.start();
+
+  // Get current frame count
+  int frame_count = ClockObject::get_global_clock()->get_frame_count();
+
+  if (_last_frame_count != frame_count) {
+    // Frame count changed, this means we are at the beginning of a new frame.
+    // In this case, update the frame count and reset the view index.
+    _last_frame_count = frame_count;
+    _current_view_index = 0;
+  }
+
+  // Get transform and render state for this render pass
+  CPT(TransformState) modelview_transform = data.get_internal_transform(trav);
+  CPT(RenderState) state = data._state->compose(get_state());
+
+  // Store a handle to the scene setup
+  const SceneSetup* scene = trav->get_scene();
+
+  // Get the MVP matrix, this is required for the LOD
+  const Lens* current_lens = scene->get_lens();
+  const LMatrix4& projection_mat = current_lens->get_projection_mat();
+
+  // Get the current lens bounds
+  PT(BoundingVolume) cam_bounds = scene->get_cull_bounds();
+
+  // Transform the camera bounds with the main camera transform
+  DCAST(GeometricBoundingVolume, cam_bounds)->xform(scene->get_camera_transform()->get_mat());
+
+  TraversalData traversal_data;
+  traversal_data.cam_bounds = cam_bounds;
+  traversal_data.model_mat = get_transform()->get_mat();
+  traversal_data.mvp_mat = modelview_transform->get_mat() * projection_mat;
+  traversal_data.emitted_chunks = 0;
+  traversal_data.storage_ptr = (ChunkDataEntry*)_data_texture->modify_ram_image().p();
+  traversal_data.screen_size.set(scene->get_viewport_width(), scene->get_viewport_height());
+
+  // Move write pointer so it points to the beginning of the current view
+  traversal_data.storage_ptr += _data_texture->get_x_size() * _current_view_index;
+
+  if (_update_enabled) {
+    // Traverse recursively
+    _lod_collector.start();
+    do_traverse(&_base_chunk, &traversal_data);
+    _lod_collector.stop();
+  } else {
+    // Do a rough guess of the emitted chunks, we don't know the actual count
+    // (we would have to store it). This is only for debugging anyways, so
+    // its not important we get an accurate count here.
+    traversal_data.emitted_chunks = _data_texture->get_x_size();
+  }
+
+  // Set shader inputs
+  CPT(RenderAttrib) current_shader_attrib = state->get_attrib_def(ShaderAttrib::get_class_slot());
+
+  // Make sure the user didn't forget to set a shader
+  if (!DCAST(ShaderAttrib, current_shader_attrib)->has_shader()) {
+    shader_terrain_cat.warning() << "No shader set on the terrain! You need to set the appropriate shader!" << endl;
+  }
+
+  // Should never happen
+  nassertv(current_shader_attrib != NULL);
+
+  current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
+    new ShaderInput("ShaderTerrainMesh.terrain_size", LVecBase2i(_size)) );
+  current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
+    new ShaderInput("ShaderTerrainMesh.chunk_size", LVecBase2i(_chunk_size)));
+  current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
+    new ShaderInput("ShaderTerrainMesh.view_index", LVecBase2i(_current_view_index)));
+  current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
+    new ShaderInput("ShaderTerrainMesh.data_texture", _data_texture));
+  current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
+    new ShaderInput("ShaderTerrainMesh.heightfield", _heightfield_tex));
+  current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_instance_count(
+    traversal_data.emitted_chunks);
+
+  state = state->set_attrib(current_shader_attrib, 10000);
+
+  // Emit chunk
+  CullableObject *object = new CullableObject(_chunk_geom, state, modelview_transform);
+  trav->get_cull_handler()->record_object(object, trav);
+
+  // After rendering, increment the view index
+  ++_current_view_index;
+
+  if (_current_view_index > stm_max_views) {
+    shader_terrain_cat.error() << "More views than supported! Increase the stm-max-views config variable!" << endl;
+  }
+
+  _basic_collector.stop();
+}
+
+/**
+ * @brief Traverses the quadtree
+ * @details This method traverses the given chunk, deciding whether it should
+ *   be rendered or subdivided.
+ *
+ *   In case the chunk is decided to be subdivided, this method is called on
+ *   all children.
+ *
+ *   In case the chunk is decided to be rendered, ShaderTerrainMesh::do_emit_chunk() is
+ *   called. Otherwise nothing happens, and the chunk does not get rendered.
+ *
+ * @param chunk Chunk to traverse
+ * @param data Traversal data
+ */
+void ShaderTerrainMesh::do_traverse(Chunk* chunk, TraversalData* data, bool fully_visible) {
+
+  // Don't check bounds if we are fully visible
+  if (!fully_visible) {
+
+    // Construct chunk bounding volume
+    PN_stdfloat scale = 1.0 / (PN_stdfloat)_size;
+    LPoint3 bb_min(chunk->x * scale, chunk->y * scale, chunk->min_height);
+    LPoint3 bb_max((chunk->x + chunk->size) * scale, (chunk->y + chunk->size) * scale, chunk->max_height);
+
+    BoundingBox bbox = BoundingBox(bb_min, bb_max);
+    DCAST(GeometricBoundingVolume, &bbox)->xform(data->model_mat);
+    int intersection = data->cam_bounds->contains(&bbox);
+
+    if (intersection == BoundingVolume::IF_no_intersection) {
+      // No intersection with frustum
+      return;
+    }
+
+    // If the bounds are fully visible, there is no reason to perform culling
+    // on the children, so we set this flag to prevent any bounding computation
+    // on the child nodes.
+    fully_visible = (intersection & BoundingVolume::IF_all) != 0;
+  }
+
+  // Check if the chunk should be subdivided. In case the chunk is a leaf node,
+  // the chunk will never get subdivided.
+  // NOTE: We still always perform the LOD check. This is for the reason that
+  // the lod check also computes the CLOD factor, which is useful.
+  if (do_check_lod_matches(chunk, data) || chunk->size == _chunk_size) {
+    do_emit_chunk(chunk, data);
+  } else {
+    // Traverse children
+    for (size_t i = 0; i < 4; ++i) {
+      do_traverse(chunk->children[i], data, fully_visible);
+    }
+  }
+}
+
+/**
+ * @brief Checks whether a chunk should get subdivided
+ * @details This method checks whether a chunk fits on screen, or should be
+ *   subdivided in order to provide bigger detail.
+ *
+ *   In case this method returns true, the chunk lod is fine, and the chunk
+ *   can be rendered. If the method returns false, the chunk should be subdivided.
+ *
+ * @param chunk Chunk to check
+ * @param data Traversal data
+ *
+ * @return true if the chunk is sufficient, false if the chunk should be subdivided
+ */
+bool ShaderTerrainMesh::do_check_lod_matches(Chunk* chunk, TraversalData* data) {
+
+  // Project all points to world space
+  LVector2 projected_points[4];
+  for (size_t y = 0; y < 2; ++y) {
+    for (size_t x = 0; x < 2; ++x) {
+
+      // Compute point in model space (0,0,0 to 1,1,1)
+      LVector3 edge_pos = LVector3(
+        (PN_stdfloat)(chunk->x + x * (chunk->size - 1)) / (PN_stdfloat)_size,
+        (PN_stdfloat)(chunk->y + y * (chunk->size - 1)) / (PN_stdfloat)_size,
+        chunk->edges.get_cell(x + 2 * y)
+      );
+      LVector4 projected = data->mvp_mat.xform(LVector4(edge_pos, 1.0));
+      if (projected.get_w() == 0.0) {
+        projected.set(0.0, 0.0, -1.0, 1.0f);
+      }
+      projected *= 1.0 / projected.get_w();
+      projected_points[x + 2 * y].set(
+        projected.get_x() * data->screen_size.get_x(),
+        projected.get_y() * data->screen_size.get_y());
+    }
+  }
+
+  // Compute the length of the edges in screen space
+  PN_stdfloat edge_top = (projected_points[1] - projected_points[3]).length_squared();
+  PN_stdfloat edge_right = (projected_points[0] - projected_points[2]).length_squared();
+  PN_stdfloat edge_bottom = (projected_points[2] - projected_points[3]).length_squared();
+  PN_stdfloat edge_left = (projected_points[0] - projected_points[1]).length_squared();
+
+  // CLOD factor
+  PN_stdfloat max_edge = max(edge_top, max(edge_right, max(edge_bottom, edge_left)));
+
+  // Micro-Optimization: We use length_squared() instead of length() to compute the
+  // maximum edge length. This reduces it to one csqrt instead of four.
+  max_edge = csqrt(max_edge);
+
+  PN_stdfloat tesselation_factor = (max_edge / _target_triangle_width) / (PN_stdfloat)_chunk_size;
+  PN_stdfloat clod_factor = max(0.0, min(1.0, 2.0 - tesselation_factor));
+
+  // Store the clod factor
+  chunk->last_clod = clod_factor;
+
+  return tesselation_factor <= 2.0;
+}
+
+/**
+ * @brief Internal method to spawn a chunk
+ * @details This method is used to spawn a chunk in case the traversal decided
+ *   that the chunk gets rendered. It writes the chunks data to the texture, and
+ *   increments the write pointer
+ *
+ * @param chunk Chunk to spawn
+ * @param data Traversal data
+ */
+void ShaderTerrainMesh::do_emit_chunk(Chunk* chunk, TraversalData* data) {
+  if (data->emitted_chunks >= _data_texture->get_x_size()) {
+
+    // Only print warning once
+    if (data->emitted_chunks == _data_texture->get_x_size()) {
+      shader_terrain_cat.error() << "Too many chunks in the terrain! Consider lowering the desired LOD, or increase the stm-max-chunk-count variable." << endl;
+      data->emitted_chunks++;
+    }
+    return;
+  }
+
+  ChunkDataEntry& data_entry = *data->storage_ptr;
+  data_entry.x = chunk->x;
+  data_entry.y = chunk->y;
+  data_entry.size = chunk->size / _chunk_size;
+  data_entry.clod = chunk->last_clod;
+
+  data->emitted_chunks ++;
+  data->storage_ptr ++;
+}
+
+/**
+ * @brief Transforms a texture coordinate to world space
+ * @details This transforms a texture coordinatefrom uv-space (0 to 1) to world
+ *   space. This takes the terrains transform into account, and also samples the
+ *   heightmap. This method should be called after generate().
+ *
+ * @param coord Coordinate in uv-space from 0, 0 to 1, 1
+ * @return World-Space point
+ */
+LPoint3 ShaderTerrainMesh::uv_to_world(const LTexCoord& coord) const {
+  nassertr(_heightfield_tex != NULL, LPoint3(0));
+  PT(TexturePeeker) peeker = _heightfield_tex->peek();
+  nassertr(peeker != NULL, LPoint3(0));
+
+  LColor result;
+  if (!peeker->lookup_bilinear(result, coord.get_x(), coord.get_y())) {
+    shader_terrain_cat.error() << "UV out of range, cant transform to world!" << endl;
+    return LPoint3(0);
+  }
+  LPoint3 unit_point(coord.get_x(), coord.get_y(), result.get_x());
+  return get_transform()->get_mat().xform_point_general(unit_point);
+}

+ 205 - 0
panda/src/grutil/shaderTerrainMesh.h

@@ -0,0 +1,205 @@
+/**
+ * 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 shaderTerrainMesh.h
+ * @author tobspr
+ * @date 2016-02-16
+ */
+
+#ifndef SHADER_TERRAIN_MESH_H
+#define SHADER_TERRAIN_MESH_H
+
+#include "pandabase.h"
+#include "luse.h"
+#include "pnmImage.h"
+#include "geom.h"
+#include "pandaNode.h"
+#include "texture.h"
+#include "texturePeeker.h"
+#include "configVariableBool.h"
+#include "configVariableInt.h"
+#include "pStatCollector.h"
+#include "filename.h"
+#include <stdint.h>
+
+extern ConfigVariableBool stm_use_hexagonal_layout;
+extern ConfigVariableInt stm_max_chunk_count;
+extern ConfigVariableInt stm_max_views;
+
+
+NotifyCategoryDecl(shader_terrain, EXPCL_PANDA_GRUTIL, EXPTP_PANDA_GRUTIL);
+
+
+/**
+ * @brief Terrain Renderer class utilizing the GPU
+ * @details This class provides functionality to render heightfields of large
+ *   sizes utilizing the GPU. Internally a quadtree is used to generate the LODs.
+ *   The final terrain is then rendered using instancing on the GPU. This makes
+ *   it possible to use very large heightfields (8192+) with very reasonable
+ *   performance. The terrain provides options to control the LOD using a
+ *   target triangle width, see ShaderTerrainMesh::set_target_triangle_width().
+ *
+ *   Because the Terrain is rendered entirely on the GPU, it needs a special
+ *   vertex shader. There is a default vertex shader available, which you can
+ *   use in your own shaders. IMPORTANT: If you don't set an appropriate shader
+ *   on the terrain, nothing will be visible.
+ */
+class EXPCL_PANDA_GRUTIL ShaderTerrainMesh : public PandaNode {
+
+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_chunk_size(size_t chunk_size);
+  INLINE size_t get_chunk_size() const;
+  MAKE_PROPERTY(chunk_size, get_chunk_size, set_chunk_size);
+
+  INLINE void set_generate_patches(bool generate_patches);
+  INLINE bool get_generate_patches() const;
+  MAKE_PROPERTY(generate_patches, get_generate_patches, set_generate_patches);
+
+  INLINE void set_update_enabled(bool update_enabled);
+  INLINE bool get_update_enabled() const;
+  MAKE_PROPERTY(update_enabled, get_update_enabled, set_update_enabled);
+
+  INLINE void set_target_triangle_width(PN_stdfloat target_triangle_width);
+  INLINE PN_stdfloat get_target_triangle_width() const;
+  MAKE_PROPERTY(target_triangle_width, get_target_triangle_width, set_target_triangle_width);
+
+  INLINE Texture* get_heightfield_tex() const;
+  MAKE_PROPERTY(heightfield_tex, get_heightfield_tex);
+
+  LPoint3 uv_to_world(const LTexCoord& coord) const;
+  INLINE LPoint3 uv_to_world(PN_stdfloat u, PN_stdfloat v) const;
+
+  bool generate();
+
+public:
+
+  // Methods derived from PandaNode
+  virtual bool is_renderable() const;
+  virtual bool safe_to_flatten() const;
+  virtual bool safe_to_combine() const;
+  virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
+
+private:
+
+  // Chunk data
+  struct Chunk {
+    // Depth, starting at 0
+    size_t depth;
+
+    // Chunk position in heightfield space
+    size_t x, y;
+
+    // Chunk size in heightfield space
+    size_t size;
+
+    // Children, in the order (0, 0) (1, 0) (0, 1) (1, 1)
+    Chunk* children[4];
+
+    // Chunk heights, used for culling
+    PN_stdfloat avg_height, min_height, max_height;
+
+    // Edge heights, used for lod computation, in the same order as the children
+    LVector4 edges;
+
+    // Last CLOD factor, stored while computing LOD, used for seamless transitions between lods
+    PN_stdfloat last_clod;
+
+    INLINE void clear_children();
+    INLINE Chunk();
+    INLINE ~Chunk();
+  };
+
+
+  // Single entry in the data block
+  struct ChunkDataEntry {
+    // float x, y, size, clod;
+
+    // Panda uses BGRA, the above layout shows how its actually in texture memory,
+    // the layout below makes it work with BGRA.
+    PN_float32 size, y, x, clod;
+  };
+
+  // Data used while traversing all chunks
+  struct TraversalData {
+    // Global MVP used for LOD
+    LMatrix4 mvp_mat;
+
+    // Local model matrix used for culling
+    LMatrix4 model_mat;
+
+    // Camera bounds in world space
+    BoundingVolume* cam_bounds;
+
+    // Amount of emitted chunks so far
+    int emitted_chunks;
+
+    // Screen resolution, used for LOD
+    LVector2i screen_size;
+
+    // Pointer to the texture memory, where each chunk is written to
+    ChunkDataEntry* storage_ptr;
+  };
+
+  bool do_load_heightfield();
+  void do_convert_heightfield();
+  void do_init_data_texture();
+  void do_create_chunks();
+  void do_init_chunk(Chunk* chunk);
+  void do_compute_bounds(Chunk* chunk);
+  void do_create_chunk_geom();
+  void do_traverse(Chunk* chunk, TraversalData* data, bool fully_visible = false);
+  void do_emit_chunk(Chunk* chunk, TraversalData* data);
+  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;
+  PNMImage _heightfield;
+  PT(Texture) _heightfield_tex;
+  PT(Geom) _chunk_geom;
+  PT(Texture) _data_texture;
+  size_t _current_view_index;
+  int _last_frame_count;
+  PN_stdfloat _target_triangle_width;
+  bool _update_enabled;
+
+  // PStats stuff
+  static PStatCollector _lod_collector;
+  static PStatCollector _basic_collector;
+
+
+// Type handle stuff
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PandaNode::init_type();
+    register_type(_type_handle, "ShaderTerrainMesh", PandaNode::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 "shaderTerrainMesh.I"
+
+#endif // SHADER_TERRAIN_MESH_H

+ 4 - 0
panda/src/pgraph/alphaTestAttrib.h

@@ -36,6 +36,10 @@ PUBLISHED:
   INLINE PN_stdfloat get_reference_alpha() const;
   INLINE PandaCompareFunc get_mode() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(reference_alpha, get_reference_alpha);
+  MAKE_PROPERTY(mode, get_mode);
+
 public:
   virtual void output(ostream &out) const;
 

+ 5 - 0
panda/src/pgraph/antialiasAttrib.h

@@ -52,6 +52,11 @@ PUBLISHED:
   INLINE unsigned short get_mode_type() const;
   INLINE unsigned short get_mode_quality() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(mode, get_mode);
+  MAKE_PROPERTY(mode_type, get_mode_type);
+  MAKE_PROPERTY(mode_quality, get_mode_quality);
+
 public:
   virtual void output(ostream &out) const;
 

+ 3 - 0
panda/src/pgraph/audioVolumeAttrib.h

@@ -40,6 +40,9 @@ PUBLISHED:
   INLINE PN_stdfloat get_volume() const;
   CPT(RenderAttrib) set_volume(PN_stdfloat volume) const;
 
+PUBLISHED:
+  MAKE_PROPERTY2(volume, has_volume, get_volume);
+
 public:
   virtual void output(ostream &out) const;
 

+ 3 - 0
panda/src/pgraph/auxBitplaneAttrib.h

@@ -63,6 +63,9 @@ PUBLISHED:
 
   INLINE int get_outputs() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(outputs, get_outputs);
+
 public:
   virtual void output(ostream &out) const;
 

+ 8 - 3
panda/src/pgraph/camera.cxx

@@ -272,8 +272,10 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   dg.add_bool(_active);
   dg.add_uint32(_camera_mask.get_word());
 
-  manager->write_pointer(dg, _initial_state);
-  dg.add_stdfloat(_lod_scale);
+  if (manager->get_file_minor_ver() >= 41) {
+    manager->write_pointer(dg, _initial_state);
+    dg.add_stdfloat(_lod_scale);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -286,7 +288,10 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 int Camera::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = LensNode::complete_pointers(p_list, manager);
-  _initial_state = DCAST(RenderState, p_list[pi++]);
+
+  if (manager->get_file_minor_ver() >= 41) {
+    _initial_state = DCAST(RenderState, p_list[pi++]);
+  }
   return pi;
 }
 

+ 4 - 0
panda/src/pgraph/colorAttrib.h

@@ -42,6 +42,10 @@ PUBLISHED:
   INLINE Type get_color_type() const;
   INLINE const LColor &get_color() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(color_type, get_color_type);
+  MAKE_PROPERTY(color, get_color);
+
 public:
   virtual void output(ostream &out) const;
 

+ 7 - 0
panda/src/pgraph/colorBlendAttrib.cxx

@@ -175,6 +175,13 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   dg.add_uint8(_mode);
   dg.add_uint8(_a);
   dg.add_uint8(_b);
+
+  if (manager->get_file_minor_ver() >= 42) {
+    dg.add_uint8(_alpha_mode);
+    dg.add_uint8(_alpha_a);
+    dg.add_uint8(_alpha_b);
+  }
+
   _color.write_datagram(dg);
 }
 

+ 11 - 0
panda/src/pgraph/colorBlendAttrib.h

@@ -104,6 +104,17 @@ PUBLISHED:
   INLINE static bool involves_constant_color(Operand operand);
   INLINE static bool involves_color_scale(Operand operand);
 
+PUBLISHED:
+  MAKE_PROPERTY(rgb_mode, get_mode);
+  MAKE_PROPERTY(rgb_operand_a, get_operand_a);
+  MAKE_PROPERTY(rgb_operand_b, get_operand_b);
+
+  MAKE_PROPERTY(alpha_mode, get_alpha_mode);
+  MAKE_PROPERTY(alpha_operand_a, get_alpha_operand_a);
+  MAKE_PROPERTY(alpha_operand_b, get_alpha_operand_b);
+
+  MAKE_PROPERTY(color, get_color);
+
 public:
   virtual void output(ostream &out) const;
 

+ 3 - 0
panda/src/pgraph/colorScaleAttrib.h

@@ -43,6 +43,9 @@ PUBLISHED:
   INLINE const LVecBase4 &get_scale() const;
   CPT(RenderAttrib) set_scale(const LVecBase4 &scale) const;
 
+PUBLISHED:
+  MAKE_PROPERTY2(scale, has_scale, get_scale);
+
 public:
   virtual bool lower_attrib_can_override() const;
   virtual void output(ostream &out) const;

+ 3 - 0
panda/src/pgraph/colorWriteAttrib.h

@@ -48,6 +48,9 @@ PUBLISHED:
 
   INLINE unsigned int get_channels() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(channels, get_channels);
+
 public:
   virtual void output(ostream &out) const;
 

+ 4 - 0
panda/src/pgraph/cullBinAttrib.h

@@ -35,6 +35,10 @@ PUBLISHED:
   INLINE const string &get_bin_name() const;
   INLINE int get_draw_order() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(bin_name, get_bin_name);
+  MAKE_PROPERTY(draw_order, get_draw_order);
+
 public:
   virtual void output(ostream &out) const;
 

+ 5 - 0
panda/src/pgraph/cullFaceAttrib.h

@@ -44,6 +44,11 @@ PUBLISHED:
   INLINE bool get_reverse() const;
   Mode get_effective_mode() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(mode, get_actual_mode);
+  MAKE_PROPERTY(reverse, get_reverse);
+  MAKE_PROPERTY(effective_mode, get_effective_mode);
+
 public:
   virtual void output(ostream &out) const;
 

+ 5 - 0
panda/src/pgraph/depthOffsetAttrib.h

@@ -60,6 +60,11 @@ PUBLISHED:
   INLINE PN_stdfloat get_min_value() const;
   INLINE PN_stdfloat get_max_value() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(offset, get_offset);
+  MAKE_PROPERTY(min_value, get_min_value);
+  MAKE_PROPERTY(max_value, get_max_value);
+
 public:
   virtual void output(ostream &out) const;
 

+ 3 - 0
panda/src/pgraph/depthTestAttrib.h

@@ -33,6 +33,9 @@ PUBLISHED:
 
   INLINE PandaCompareFunc get_mode() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(mode, get_mode);
+
 public:
   virtual void output(ostream &out) const;
 

+ 3 - 0
panda/src/pgraph/depthWriteAttrib.h

@@ -39,6 +39,9 @@ PUBLISHED:
 
   INLINE Mode get_mode() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(mode, get_mode);
+
 public:
   virtual void output(ostream &out) const;
 

+ 3 - 0
panda/src/pgraph/fogAttrib.h

@@ -34,6 +34,9 @@ PUBLISHED:
   INLINE bool is_off() const;
   INLINE Fog *get_fog() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(fog, get_fog);
+
 public:
   virtual void output(ostream &out) const;
 

+ 3 - 0
panda/src/pgraph/lightRampAttrib.h

@@ -52,6 +52,9 @@ PUBLISHED:
   INLINE PN_stdfloat get_level(int n) const;
   INLINE PN_stdfloat get_threshold(int n) const;
 
+PUBLISHED:
+  MAKE_PROPERTY(mode, get_mode);
+
 public:
   virtual void output(ostream &out) const;
 

+ 3 - 0
panda/src/pgraph/materialAttrib.h

@@ -36,6 +36,9 @@ PUBLISHED:
   INLINE bool is_off() const;
   INLINE Material *get_material() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(material, get_material);
+
 public:
   virtual void output(ostream &out) const;
 

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

@@ -63,9 +63,14 @@ PUBLISHED:
   INLINE PN_stdfloat get_thickness() const;
   INLINE bool get_perspective() const;
   INLINE const LColor &get_wireframe_color() const;
-
   INLINE int get_geom_rendering(int geom_rendering) const;
 
+PUBLISHED:
+  MAKE_PROPERTY(mode, get_mode);
+  MAKE_PROPERTY(thickness, get_thickness);
+  MAKE_PROPERTY(perspective, get_perspective);
+  MAKE_PROPERTY(wireframe_color, get_wireframe_color);
+
 public:
   virtual void output(ostream &out) const;
 

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

@@ -49,6 +49,7 @@ PUBLISHED:
   INLINE static CPT(RenderAttrib) make_default();
 
   INLINE Mode get_mode() const;
+  MAKE_PROPERTY(mode, get_mode);
 
 public:
   virtual void output(ostream &out) const;

+ 3 - 0
panda/src/pgraph/scissorAttrib.h

@@ -47,6 +47,9 @@ PUBLISHED:
 
   INLINE const LVecBase4 &get_frame() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(frame, get_frame);
+
 public:
   virtual void output(ostream &out) const;
 

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

@@ -39,6 +39,7 @@ PUBLISHED:
   static CPT(RenderAttrib) make_default();
 
   INLINE Mode get_mode() const;
+  MAKE_PROPERTY(mode, get_mode);
 
 public:
   virtual void output(ostream &out) const;

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

@@ -114,6 +114,10 @@ PUBLISHED:
 
   static void register_with_read_factory();
 
+PUBLISHED:
+  MAKE_PROPERTY(shader, get_shader);
+  MAKE_PROPERTY(instance_count, get_instance_count);
+
 public:
   virtual void output(ostream &out) const;
 

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

@@ -51,6 +51,7 @@ PUBLISHED:
   static CPT(RenderAttrib) make_default();
 
   INLINE Mode get_mode() const;
+  MAKE_PROPERTY(mode, get_mode);
 
 public:
   virtual void output(ostream &out) const;

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

@@ -29,6 +29,7 @@
 #include "selectiveChildNode.h"
 #include "sequenceNode.h"
 #include "shaderGenerator.h"
+#include "sphereLight.h"
 #include "spotlight.h"
 #include "switchNode.h"
 #include "uvScrollNode.h"
@@ -123,6 +124,7 @@ init_libpgraphnodes() {
   SelectiveChildNode::init_type();
   SequenceNode::init_type();
   ShaderGenerator::init_type();
+  SphereLight::init_type();
   Spotlight::init_type();
   SwitchNode::init_type();
   UvScrollNode::init_type();
@@ -137,6 +139,7 @@ init_libpgraphnodes() {
   PointLight::register_with_read_factory();
   SelectiveChildNode::register_with_read_factory();
   SequenceNode::register_with_read_factory();
+  SphereLight::register_with_read_factory();
   Spotlight::register_with_read_factory();
   SwitchNode::register_with_read_factory();
   UvScrollNode::register_with_read_factory();

+ 2 - 0
panda/src/pgraphnodes/p3pgraphnodes_composite1.cxx

@@ -7,3 +7,5 @@
 #include "fadeLodNodeData.cxx"
 #include "lightLensNode.cxx"
 #include "lightNode.cxx"
+#include "lodNode.cxx"
+#include "lodNodeType.cxx"

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

@@ -1,11 +1,10 @@
-#include "lodNode.cxx"
-#include "lodNodeType.cxx"
 #include "nodeCullCallbackData.cxx"
 #include "pointLight.cxx"
 #include "sceneGraphAnalyzer.cxx"
 #include "selectiveChildNode.cxx"
 #include "sequenceNode.cxx"
 #include "shaderGenerator.cxx"
+#include "sphereLight.cxx"
 #include "spotlight.cxx"
 #include "switchNode.cxx"
 #include "uvScrollNode.cxx"

+ 48 - 0
panda/src/pgraphnodes/sphereLight.I

@@ -0,0 +1,48 @@
+/**
+ * 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 sphereLight.I
+ * @author rdb
+ * @date 2016-04-15
+ */
+
+/**
+ *
+ */
+INLINE SphereLight::CData::
+CData() :
+  _radius(0.01f)
+{
+}
+
+/**
+ *
+ */
+INLINE SphereLight::CData::
+CData(const SphereLight::CData &copy) :
+  _radius(copy._radius)
+{
+}
+
+/**
+ * Returns the radius of the sphere.
+ */
+INLINE PN_stdfloat SphereLight::
+get_radius() const {
+  CDReader cdata(_cycler);
+  return cdata->_radius;
+}
+
+/**
+ * Sets the radius of the sphere.
+ */
+INLINE void SphereLight::
+set_radius(PN_stdfloat radius) {
+  CDWriter cdata(_cycler);
+  cdata->_radius = radius;
+}

+ 146 - 0
panda/src/pgraphnodes/sphereLight.cxx

@@ -0,0 +1,146 @@
+/**
+ * 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 sphereLight.cxx
+ * @author rdb
+ * @date 2016-04-15
+ */
+
+#include "sphereLight.h"
+#include "graphicsStateGuardianBase.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle SphereLight::_type_handle;
+
+/**
+ *
+ */
+CycleData *SphereLight::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 SphereLight::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  dg.add_stdfloat(_radius);
+}
+
+/**
+ * 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 SphereLight::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  _radius = scan.get_stdfloat();
+}
+
+/**
+ *
+ */
+SphereLight::
+SphereLight(const string &name) :
+  PointLight(name)
+{
+}
+
+/**
+ * Do not call the copy constructor directly; instead, use make_copy() or
+ * copy_subgraph() to make a copy of a node.
+ */
+SphereLight::
+SphereLight(const SphereLight &copy) :
+  PointLight(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 *SphereLight::
+make_copy() const {
+  return new SphereLight(*this);
+}
+
+/**
+ * Transforms the contents of this PandaNode by the indicated matrix, if it
+ * means anything to do so.  For most kinds of PandaNodes, this does nothing.
+ */
+void SphereLight::
+xform(const LMatrix4 &mat) {
+  PointLight::xform(mat);
+  CDWriter cdata(_cycler);
+  cdata->_radius = mat.xform_vec(LVector3(0, 0, cdata->_radius)).length();
+  mark_viz_stale();
+}
+
+/**
+ *
+ */
+void SphereLight::
+write(ostream &out, int indent_level) const {
+  PointLight::write(out, indent_level);
+  indent(out, indent_level) << *this << ":\n";
+  indent(out, indent_level + 2)
+    << "radius " << get_radius() << "\n";
+}
+
+/**
+ * Tells the BamReader how to create objects of type SphereLight.
+ */
+void SphereLight::
+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 SphereLight::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PointLight::write_datagram(manager, dg);
+  manager->write_cdata(dg, _cycler);
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type SphereLight is encountered in the Bam file.  It should create the
+ * SphereLight and extract its information from the file.
+ */
+TypedWritable *SphereLight::
+make_from_bam(const FactoryParams &params) {
+  SphereLight *node = new SphereLight("");
+  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 SphereLight.
+ */
+void SphereLight::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  PointLight::fillin(scan, manager);
+
+  manager->read_cdata(scan, _cycler);
+}

+ 90 - 0
panda/src/pgraphnodes/sphereLight.h

@@ -0,0 +1,90 @@
+/**
+ * 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 sphereLight.h
+ * @author rdb
+ * @date 2016-04-15
+ */
+
+#ifndef SPHERELIGHT_H
+#define SPHERELIGHT_H
+
+#include "pandabase.h"
+
+#include "lightLensNode.h"
+
+/**
+ * A sphere light is like a point light, except that it represents a sphere
+ * with a radius, rather than being an infinitely thin point in space.
+ */
+class EXPCL_PANDA_PGRAPHNODES SphereLight : public PointLight {
+PUBLISHED:
+  SphereLight(const string &name);
+
+protected:
+  SphereLight(const SphereLight &copy);
+
+public:
+  virtual PandaNode *make_copy() const;
+  virtual void xform(const LMatrix4 &mat);
+  virtual void write(ostream &out, int indent_level) const;
+
+PUBLISHED:
+  INLINE PN_stdfloat get_radius() const;
+  INLINE void set_radius(PN_stdfloat radius);
+  MAKE_PROPERTY(radius, get_radius, set_radius);
+
+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 SphereLight::get_class_type();
+    }
+
+    PN_stdfloat _radius;
+  };
+
+  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() {
+    PointLight::init_type();
+    register_type(_type_handle, "SphereLight",
+                  PointLight::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 "sphereLight.I"
+
+#endif

+ 11 - 0
panda/src/putil/bamReader.I

@@ -159,6 +159,17 @@ get_file_pos() {
   return _source->get_file_pos();
 }
 
+/**
+ * Registers a factory function that is called when an object of the given
+ * type is encountered within the .bam stream.
+ *
+ * @param user_data an optional pointer to be passed along to the function.
+ */
+void BamReader::
+register_factory(TypeHandle handle, WritableFactory::CreateFunc *func, void *user_data) {
+  get_factory()->register_factory(handle, func, user_data);
+}
+
 /**
  * Returns the global WritableFactory for generating TypedWritable objects
  */

+ 9 - 0
panda/src/putil/bamReader.h

@@ -148,11 +148,14 @@ PUBLISHED:
   INLINE int get_current_major_ver() const;
   INLINE int get_current_minor_ver() const;
 
+  EXTENSION(PyObject *get_file_version() const);
+
 PUBLISHED:
   MAKE_PROPERTY(source, get_source, set_source);
   MAKE_PROPERTY(filename, get_filename);
   MAKE_PROPERTY(loader_options, get_loader_options, set_loader_options);
 
+  MAKE_PROPERTY(file_version, get_file_version);
   MAKE_PROPERTY(file_endian, get_file_endian);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
 
@@ -194,7 +197,13 @@ public:
   INLINE streampos get_file_pos();
 
 public:
+  INLINE static void register_factory(TypeHandle type, WritableFactory::CreateFunc *func,
+                                      void *user_data = NULL);
   INLINE static WritableFactory *get_factory();
+
+PUBLISHED:
+  EXTENSION(static void register_factory(TypeHandle handle, PyObject *func));
+
 private:
   INLINE static void create_factory();
 

+ 123 - 0
panda/src/putil/bamReader_ext.cxx

@@ -0,0 +1,123 @@
+/**
+ * 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 bamReader_ext.cxx
+ * @author rdb
+ * @date 2016-04-09
+ */
+
+#include "bamReader_ext.h"
+#include "config_util.h"
+
+#ifdef HAVE_PYTHON
+
+#ifndef CPPPARSER
+extern Dtool_PyTypedObject Dtool_BamReader;
+extern Dtool_PyTypedObject Dtool_DatagramIterator;
+extern Dtool_PyTypedObject Dtool_TypedWritable;
+#endif  // CPPPARSER
+
+/**
+ * Factory function that calls the registered Python function.
+ */
+static TypedWritable *factory_callback(const FactoryParams &params){
+  PyObject *func = (PyObject *)params.get_user_data();
+  nassertr(func != NULL, NULL);
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  // Use PyGILState to protect this asynchronous call.
+  PyGILState_STATE gstate;
+  gstate = PyGILState_Ensure();
+#endif
+
+  // Extract the parameters we will pass to the Python function.
+  DatagramIterator scan;
+  BamReader *manager;
+  parse_params(params, scan, manager);
+
+  PyObject *py_scan = DTool_CreatePyInstance(&scan, Dtool_DatagramIterator, false, false);
+  PyObject *py_manager = DTool_CreatePyInstance(manager, Dtool_BamReader, false, false);
+  PyObject *args = PyTuple_Pack(2, py_scan, py_manager);
+
+  // Now call the Python function.
+  Thread *current_thread = Thread::get_current_thread();
+  PyObject *result = current_thread->call_python_func(func, args);
+  Py_DECREF(args);
+  Py_DECREF(py_scan);
+  Py_DECREF(py_manager);
+
+  if (result == (PyObject *)NULL) {
+    util_cat.error()
+      << "Exception occurred in Python factory function\n";
+
+  } else if (result == Py_None) {
+    util_cat.error()
+      << "Python factory function returned None\n";
+    Py_DECREF(result);
+    result = NULL;
+  }
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyGILState_Release(gstate);
+#endif
+
+  // Unwrap the returned TypedWritable object.
+  if (result == (PyObject *)NULL) {
+    return (TypedWritable *)NULL;
+  } else {
+    void *object = NULL;
+    Dtool_Call_ExtractThisPointer(result, Dtool_TypedWritable, &object);
+
+    TypedWritable *ptr = (TypedWritable *)object;
+    ReferenceCount *ref_count = ptr->as_reference_count();
+    if (ref_count != NULL) {
+      // If the Python pointer is the last reference to it, make sure that the
+      // object isn't destroyed.  We do this by calling unref(), which
+      // decreases the reference count without destroying the object.
+      if (result->ob_refcnt <= 1) {
+        ref_count->unref();
+
+        // Tell the Python wrapper object that it shouldn't try to delete the
+        // object when it is destroyed.
+        ((Dtool_PyInstDef *)result)->_memory_rules = false;
+      }
+      Py_DECREF(result);
+    }
+
+    return (TypedWritable *)object;
+  }
+}
+
+/**
+ * Returns the version number of the Bam file currently being read.
+ */
+PyObject *Extension<BamReader>::
+get_file_version() const {
+  return Py_BuildValue("(ii)", _this->get_file_major_ver(),
+                               _this->get_file_minor_ver());
+}
+
+/**
+ * Registers a Python function as factory function for the given type.  This
+ * should be a function (or class object) that takes a DatagramIterator and a
+ * BamReader as arguments, and should return a TypedWritable object.
+ */
+void Extension<BamReader>::
+register_factory(TypeHandle handle, PyObject *func) {
+  nassertv(func != NULL);
+
+  if (!PyCallable_Check(func)) {
+    Dtool_Raise_TypeError("second argument to register_factory must be callable");
+    return;
+  }
+
+  Py_INCREF(func);
+  BamReader::get_factory()->register_factory(handle, &factory_callback, (void *)func);
+}
+
+#endif

+ 39 - 0
panda/src/putil/bamReader_ext.h

@@ -0,0 +1,39 @@
+/**
+ * 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 bamReader_ext.h
+ * @author rdb
+ * @date 2016-04-09
+ */
+
+#ifndef BAMREADER_EXT_H
+#define BAMREADER_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "bamReader.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for BamReader, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<BamReader> : public ExtensionBase<BamReader> {
+public:
+  PyObject *get_file_version() const;
+
+  static void register_factory(TypeHandle handle, PyObject *func);
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // BAMREADER_EXT_H

+ 2 - 2
panda/src/putil/factory.I

@@ -70,6 +70,6 @@ make_instance_more_general(const string &type_name,
  */
 template<class Type>
 INLINE void Factory<Type>::
-register_factory(TypeHandle handle, CreateFunc *func) {
-  FactoryBase::register_factory(handle, (BaseCreateFunc *)func);
+register_factory(TypeHandle handle, CreateFunc *func, void *user_data) {
+  FactoryBase::register_factory(handle, (BaseCreateFunc *)func, user_data);
 }

+ 2 - 1
panda/src/putil/factory.h

@@ -49,7 +49,8 @@ public:
   make_instance_more_general(const string &type_name,
                              const FactoryParams &params = FactoryParams());
 
-  INLINE void register_factory(TypeHandle handle, CreateFunc *func);
+  INLINE void register_factory(TypeHandle handle, CreateFunc *func,
+                               void *user_data = NULL);
 };
 
 #include "factory.I"

+ 18 - 10
panda/src/putil/factoryBase.cxx

@@ -128,12 +128,18 @@ find_registered_type(TypeHandle handle) {
 
 /**
  * Registers a new kind of thing the Factory will be able to create.
+ *
+ * @param user_data an optional pointer to be passed along to the function.
  */
 void FactoryBase::
-register_factory(TypeHandle handle, BaseCreateFunc *func) {
+register_factory(TypeHandle handle, BaseCreateFunc *func, void *user_data) {
   nassertv(handle != TypeHandle::none());
   nassertv(func != (BaseCreateFunc *)NULL);
-  _creators[handle] = func;
+
+  Creator creator;
+  creator._func = func;
+  creator._user_data = user_data;
+  _creators[handle] = creator;
 }
 
 /**
@@ -234,15 +240,16 @@ operator = (const FactoryBase &) {
  * not be created.
  */
 TypedObject *FactoryBase::
-make_instance_exact(TypeHandle handle, const FactoryParams &params) {
+make_instance_exact(TypeHandle handle, FactoryParams params) {
   Creators::const_iterator ci = _creators.find(handle);
   if (ci == _creators.end()) {
     return NULL;
   }
 
-  BaseCreateFunc *func = (BaseCreateFunc *)((*ci).second);
-  nassertr(func != (BaseCreateFunc *)NULL, NULL);
-  return (*func)(params);
+  Creator creator = (*ci).second;
+  nassertr(creator._func != (BaseCreateFunc *)NULL, NULL);
+  params._user_data = creator._user_data;
+  return (*creator._func)(params);
 }
 
 /**
@@ -251,7 +258,7 @@ make_instance_exact(TypeHandle handle, const FactoryParams &params) {
  * instance could not be created.
  */
 TypedObject *FactoryBase::
-make_instance_more_specific(TypeHandle handle, const FactoryParams &params) {
+make_instance_more_specific(TypeHandle handle, FactoryParams params) {
   // First, walk through the established preferred list.  Maybe one of these
   // qualifies.
 
@@ -272,9 +279,10 @@ make_instance_more_specific(TypeHandle handle, const FactoryParams &params) {
   for (ci = _creators.begin(); ci != _creators.end(); ++ci) {
     TypeHandle ctype = (*ci).first;
     if (ctype.is_derived_from(handle)) {
-      BaseCreateFunc *func = (BaseCreateFunc *)((*ci).second);
-      nassertr(func != (BaseCreateFunc *)NULL, NULL);
-      TypedObject *object = (*func)(params);
+      Creator creator = (*ci).second;
+      nassertr(creator._func != (BaseCreateFunc *)NULL, NULL);
+      params._user_data = creator._user_data;
+      TypedObject *object = (*creator._func)(params);
       if (object != (TypedObject *)NULL) {
         return object;
       }

+ 8 - 12
panda/src/putil/factoryBase.h

@@ -56,7 +56,7 @@ public:
 
   TypeHandle find_registered_type(TypeHandle handle);
 
-  void register_factory(TypeHandle handle, BaseCreateFunc *func);
+  void register_factory(TypeHandle handle, BaseCreateFunc *func, void *user_data = NULL);
 
   int get_num_types() const;
   TypeHandle get_type(int n) const;
@@ -74,22 +74,18 @@ private:
   void operator = (const FactoryBase &copy);
 
   // internal utility functions
-  TypedObject *make_instance_exact(TypeHandle handle,
-                                   const FactoryParams &params);
+  TypedObject *make_instance_exact(TypeHandle handle, FactoryParams params);
   TypedObject *make_instance_more_specific(TypeHandle handle,
-                                           const FactoryParams &params);
+                                           FactoryParams params);
 
 private:
   // internal mechanics and bookkeeping
+  struct Creator {
+    BaseCreateFunc *_func;
+    void *_user_data;
+  };
 
-#if (defined(WIN32_VC) || defined(WIN64_VC)) && !defined(__ICL)    //__ICL is Intel C++
-  // Visual C++ seems to have a problem with building a map based on
-  // BaseCreateFunc.  We'll have to typecast it on the way out.
-  typedef pmap<TypeHandle, void *> Creators;
-#else
-  typedef pmap<TypeHandle, BaseCreateFunc *> Creators;
-#endif
-
+  typedef pmap<TypeHandle, Creator> Creators;
   Creators _creators;
 
   typedef pvector<TypeHandle> Preferred;

+ 48 - 0
panda/src/putil/factoryParams.I

@@ -13,6 +13,54 @@
 
 #include "pnotify.h"
 
+/**
+ *
+ */
+INLINE FactoryParams::
+FactoryParams() : _user_data(NULL) {
+}
+
+/**
+ *
+ */
+INLINE FactoryParams::
+FactoryParams(const FactoryParams &copy) :
+  _params(copy._params),
+  _user_data(copy._user_data) {}
+
+/**
+ *
+ */
+INLINE FactoryParams::
+~FactoryParams() {
+}
+
+#ifdef USE_MOVE_SEMANTICS
+/**
+ *
+ */
+INLINE FactoryParams::
+FactoryParams(FactoryParams &&from) NOEXCEPT :
+  _params(move(from._params)),
+  _user_data(from._user_data) {}
+
+/**
+ *
+ */
+INLINE void FactoryParams::
+operator = (FactoryParams &&from) NOEXCEPT {
+  _params = move(from._params);
+  _user_data = from._user_data;
+}
+#endif
+
+/**
+ * Returns the custom pointer that was associated with the factory function.
+ */
+INLINE void *FactoryParams::
+get_user_data() const {
+  return _user_data;
+}
 
 /**
  * A handy convenience template function that extracts a parameter of the

+ 0 - 14
panda/src/putil/factoryParams.cxx

@@ -13,20 +13,6 @@
 
 #include "factoryParams.h"
 
-/**
- *
- */
-FactoryParams::
-FactoryParams() {
-}
-
-/**
- *
- */
-FactoryParams::
-~FactoryParams() {
-}
-
 /**
  *
  */

+ 13 - 2
panda/src/putil/factoryParams.h

@@ -35,8 +35,14 @@
  */
 class EXPCL_PANDA_PUTIL FactoryParams {
 public:
-  FactoryParams();
-  ~FactoryParams();
+  INLINE FactoryParams();
+  INLINE FactoryParams(const FactoryParams &copy);
+  INLINE ~FactoryParams();
+
+#ifdef USE_MOVE_SEMANTICS
+  INLINE FactoryParams(FactoryParams &&from) NOEXCEPT;
+  INLINE void operator = (FactoryParams &&from) NOEXCEPT;
+#endif
 
   void add_param(FactoryParam *param);
   void clear();
@@ -46,10 +52,15 @@ public:
 
   FactoryParam *get_param_of_type(TypeHandle type) const;
 
+  INLINE void *get_user_data() const;
+
 private:
   typedef pvector< PT(TypedReferenceCount) > Params;
 
   Params _params;
+  void *_user_data;
+
+  friend class FactoryBase;
 };
 
 template<class ParamType>

+ 3 - 0
panda/src/putil/p3putil_ext_composite.cxx

@@ -0,0 +1,3 @@
+#include "bamReader_ext.cxx"
+#include "pythonCallbackObject.cxx"
+#include "typedWritable_ext.cxx"

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

@@ -47,7 +47,10 @@ public:
   virtual int complete_pointers(TypedWritable **p_list, BamReader *manager);
   virtual bool require_fully_complete() const;
 
+PUBLISHED:
   virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
   virtual void finalize(BamReader *manager);
 
   virtual ReferenceCount *as_reference_count();

+ 8 - 0
panda/src/vrpn/vrpn_interface.h

@@ -27,6 +27,14 @@
   #endif
 #endif
 
+// VPRN misses an include to this in vrpn_Shared.h.
+#include <stdint.h>
+
+// Prevent VRPN from defining this function, which we don't need,
+// and cause compilation errors in MSVC 2015.
+#include "vrpn_Configure.h"
+#undef VRPN_EXPORT_GETTIMEOFDAY
+
 #include "vrpn_Connection.h"
 #include "vrpn_Tracker.h"
 #include "vrpn_Analog.h"

+ 7 - 5
panda/src/wgldisplay/wglGraphicsBuffer.cxx

@@ -74,10 +74,12 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     return false;
   }
 
-  if (_fb_properties.is_single_buffered()) {
-    wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB);
-  } else {
-    wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_BACK_LEFT_ARB);
+  if (_pbuffer_bound) {
+    if (_fb_properties.is_single_buffered()) {
+      wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB);
+    } else {
+      wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_BACK_LEFT_ARB);
+    }
   }
 
   if (!rebuild_bitplanes()) {
@@ -146,7 +148,7 @@ bind_texture_to_pbuffer() {
   for (size_t i = 0; i != cdata->_textures.size(); ++i) {
     const RenderTexture &rt = cdata->_textures[i];
     RenderTexturePlane plane = rt._plane;
-    if (plane == RTP_color) {
+    if (plane == RTP_color && rt._rtm_mode == RTM_bind_or_copy) {
       tex_index = i;
       break;
     }

BIN
samples/shader-terrain/heightfield.png


+ 81 - 0
samples/shader-terrain/main.py

@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+
+# Author: tobspr
+#
+# Last Updated: 2016-02-13
+#
+# 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):
+
+        # Load some configuration variables, its important for this to happen
+        # before the ShowBase is initialized
+        load_prc_file_data("", """
+            textures-power-2 none
+            window-title Panda3D Shader Terrain Demo
+            gl-coordinate-system default
+        """)
+
+        # Initialize the showbase
+        ShowBase.__init__(self)
+
+        # Increase camera FOV aswell as the far plane
+        self.camLens.set_fov(90)
+        self.camLens.set_near_far(0.1, 50000)
+
+        # Construct the terrain
+        self.terrain_node = ShaderTerrainMesh()
+
+        # 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"
+
+        # 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.
+        self.terrain_node.target_triangle_width = 10.0
+
+        # Generate the terrain
+        self.terrain_node.generate()
+
+        # Attach the terrain to the main scene and set its scale
+        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
+        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)
+
+        # 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
+        skybox = self.loader.loadModel("models/skybox.bam")
+        skybox.reparent_to(self.render)
+        skybox.set_scale(20000)
+
+        skybox_texture = self.loader.loadTexture("textures/skybox.jpg")
+        skybox_texture.set_minfilter(SamplerState.FT_linear)
+        skybox_texture.set_magfilter(SamplerState.FT_linear)
+        skybox_texture.set_wrap_u(SamplerState.WM_repeat)
+        skybox_texture.set_wrap_v(SamplerState.WM_mirror)
+        skybox_texture.set_anisotropic_degree(16)
+        skybox.set_texture(skybox_texture)
+
+        skybox_shader = Shader.load(Shader.SL_GLSL, "skybox.vert.glsl", "skybox.frag.glsl")
+        skybox.set_shader(skybox_shader)
+
+demo = ShaderTerrainDemo()
+demo.run()

BIN
samples/shader-terrain/models/skybox.bam


+ 21 - 0
samples/shader-terrain/skybox.frag.glsl

@@ -0,0 +1,21 @@
+#version 150
+
+in vec3 skybox_pos;
+out vec4 color;
+
+uniform sampler2D p3d_Texture0;
+
+void main() {
+
+  vec3 view_dir = normalize(skybox_pos);
+  vec2 skybox_uv;
+
+  // Convert spherical coordinates
+  const float pi = 3.14159265359;
+  skybox_uv.x = (atan(view_dir.y, view_dir.x) + (0.5 * pi)) / (2 * pi);
+  skybox_uv.y = clamp(view_dir.z * 0.72 + 0.35, 0.0, 1.0);
+
+  vec3 skybox_color = textureLod(p3d_Texture0, skybox_uv, 0).xyz;
+
+  color = vec4(skybox_color, 1);
+}

+ 13 - 0
samples/shader-terrain/skybox.vert.glsl

@@ -0,0 +1,13 @@
+#version 150
+
+// This is just a simple vertex shader transforming the skybox
+
+in vec4 p3d_Vertex;
+uniform mat4 p3d_ModelViewProjectionMatrix;
+
+out vec3 skybox_pos;
+
+void main() {
+  skybox_pos = p3d_Vertex.xyz;
+  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;
+}

+ 56 - 0
samples/shader-terrain/terrain.frag.glsl

@@ -0,0 +1,56 @@
+#version 150
+
+// This is the terrain fragment shader. There is a lot of code in here
+// which is not necessary to render the terrain, but included for convenience -
+// Like generating normals from the heightmap or a simple fog effect.
+
+// Most of the time you want to adjust this shader to get your terrain the look
+// you want. The vertex shader most likely will stay the same.
+
+in vec2 terrain_uv;
+in vec3 vtx_pos;
+out vec4 color;
+
+uniform struct {
+  sampler2D data_texture;
+  sampler2D heightfield;
+  int view_index;
+  int terrain_size;
+  int chunk_size;
+} ShaderTerrainMesh;
+
+uniform sampler2D p3d_Texture0;
+uniform vec3 wspos_camera;
+
+// Compute normal from the heightmap, this assumes the terrain is facing z-up
+vec3 get_terrain_normal() {
+  const float terrain_height = 50.0;
+  vec3 pixel_size = vec3(1.0, -1.0, 0) / textureSize(ShaderTerrainMesh.heightfield, 0).xxx;
+  float u0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.yz).x * terrain_height;
+  float u1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.xz).x * terrain_height;
+  float v0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zy).x * terrain_height;
+  float v1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zx).x * terrain_height;
+  vec3 tangent = normalize(vec3(1.0, 0, u1 - u0));
+  vec3 binormal = normalize(vec3(0, 1.0, v1 - v0));
+  return normalize(cross(tangent, binormal));
+}
+
+
+
+void main() {
+  vec3 diffuse = texture(p3d_Texture0, terrain_uv * 16.0).xyz;
+  vec3 normal = get_terrain_normal();
+
+  // Add some fake lighting - you usually want to use your own lighting code here
+  vec3 fake_sun = normalize(vec3(0.7, 0.2, 0.6));
+  vec3 shading = max(0.0, dot(normal, fake_sun)) * diffuse;
+  shading += vec3(0.07, 0.07, 0.1);
+
+
+  // Fake fog
+  float dist = distance(vtx_pos, wspos_camera);
+  float fog_factor = smoothstep(0, 1, dist / 1000.0);
+  shading = mix(shading, vec3(0.7, 0.7, 0.8), fog_factor);
+
+  color = vec4(shading, 1.0);
+}

+ 56 - 0
samples/shader-terrain/terrain.vert.glsl

@@ -0,0 +1,56 @@
+#version 150
+
+// This is the default terrain vertex shader. Most of the time you can just copy
+// this and reuse it, and just modify the fragment shader.
+
+in vec4 p3d_Vertex;
+uniform mat4 p3d_ModelViewProjectionMatrix;
+uniform mat4 p3d_ModelMatrix;
+
+uniform struct {
+  sampler2D data_texture;
+  sampler2D heightfield;
+  int view_index;
+  int terrain_size;
+  int chunk_size;
+} ShaderTerrainMesh;
+
+out vec2 terrain_uv;
+out vec3 vtx_pos;
+
+void main() {
+
+  // Terrain data has the layout:
+  // x: x-pos, y: y-pos, z: size, w: clod
+  vec4 terrain_data = texelFetch(ShaderTerrainMesh.data_texture,
+    ivec2(gl_InstanceID, ShaderTerrainMesh.view_index), 0);
+
+  // Get initial chunk position in the (0, 0, 0), (1, 1, 0) range
+  vec3 chunk_position = p3d_Vertex.xyz;
+
+  // CLOD implementation
+  float clod_factor = smoothstep(0, 1, terrain_data.w);
+  chunk_position.xy -= clod_factor * fract(chunk_position.xy * ShaderTerrainMesh.chunk_size / 2.0)
+                          * 2.0 / ShaderTerrainMesh.chunk_size;
+
+  // Scale the chunk
+  chunk_position *= terrain_data.z * float(ShaderTerrainMesh.chunk_size)
+                    / float(ShaderTerrainMesh.terrain_size);
+  chunk_position.z *= ShaderTerrainMesh.chunk_size;
+
+  // Offset the chunk, it is important that this happens after the scale
+  chunk_position.xy += terrain_data.xy / float(ShaderTerrainMesh.terrain_size);
+
+  // Compute the terrain UV coordinates
+  terrain_uv = chunk_position.xy;
+
+  // Sample the heightfield and offset the terrain - we do not need to multiply
+  // the height with anything since the terrain transform is included in the
+  // model view projection matrix.
+  chunk_position.z += texture(ShaderTerrainMesh.heightfield, terrain_uv).x;
+  gl_Position = p3d_ModelViewProjectionMatrix * vec4(chunk_position, 1);
+
+  // Output the vertex world space position - in this case we use this to render
+  // the fog.
+  vtx_pos = (p3d_ModelMatrix * vec4(chunk_position, 1)).xyz;
+}

+ 5 - 0
samples/shader-terrain/textures/LICENSE.txt

@@ -0,0 +1,5 @@
+Grass texture from (cc by 3.0)
+http://opengameart.org/content/grass-texture
+
+Skybox by rdb
+http://rdb.name/PANO_20140818_112419.jpg

BIN
samples/shader-terrain/textures/grass.png


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff