2
0
Эх сурвалжийг харах

Merge branch 'master' into vulkan

rdb 9 жил өмнө
parent
commit
75ccc1d6bf
100 өөрчлөгдсөн 3341 нэмэгдсэн , 1808 устгасан
  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:]))
                 self.name, request, str(args)[1:]))
 
 
             filter = self.getCurrentFilter()
             filter = self.getCurrentFilter()
-            result = list(filter(request, args))
+            result = filter(request, args)
             if result:
             if result:
                 if isinstance(result, str):
                 if isinstance(result, str):
                     # If the return value is a string, it's just the name
                     # 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 direct.task.Task import Task
 from .DirectFrame import *
 from .DirectFrame import *
 from .DirectButton import *
 from .DirectButton import *
-import types
 
 
 
 
 class DirectScrolledListItem(DirectButton):
 class DirectScrolledListItem(DirectButton):
@@ -49,7 +48,7 @@ class DirectScrolledList(DirectFrame):
     def __init__(self, parent = None, **kw):
     def __init__(self, parent = None, **kw):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
         self.index = 0
         self.index = 0
-        self.forceHeight = None
+        self.__forceHeight = None
 
 
         """ If one were to want a scrolledList that makes and adds its items
         """ 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')
            as needed, simply pass in an items list of strings (type 'str')
@@ -115,12 +114,12 @@ class DirectScrolledList(DirectFrame):
 
 
     def setForceHeight(self):
     def setForceHeight(self):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
-        self.forceHeight = self["forceHeight"]
+        self.__forceHeight = self["forceHeight"]
 
 
     def recordMaxHeight(self):
     def recordMaxHeight(self):
         assert self.notify.debugStateCall(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:
         else:
             self.maxHeight = 0.0
             self.maxHeight = 0.0
             for item in self["items"]:
             for item in self["items"]:
@@ -130,24 +129,24 @@ class DirectScrolledList(DirectFrame):
     def setScrollSpeed(self):
     def setScrollSpeed(self):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
         # Items per second to move
         # 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):
     def setNumItemsVisible(self):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
         # Items per second to move
         # Items per second to move
-        self.numItemsVisible = self["numItemsVisible"]
+        self.__numItemsVisible = self["numItemsVisible"]
 
 
     def destroy(self):
     def destroy(self):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
         taskMgr.remove(self.taskName("scroll"))
         taskMgr.remove(self.taskName("scroll"))
         if hasattr(self, "currentSelected"):
         if hasattr(self, "currentSelected"):
             del 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.incButton.destroy()
         self.decButton.destroy()
         self.decButton.destroy()
         DirectFrame.destroy(self)
         DirectFrame.destroy(self)
@@ -169,10 +168,10 @@ class DirectScrolledList(DirectFrame):
         #for i in range(len(self["items"])):
         #for i in range(len(self["items"])):
         #    print "buttontext[", i,"]", self["items"][i]["text"]
         #    print "buttontext[", i,"]", self["items"][i]["text"]
 
 
-        if(len(self["items"])==0):
+        if len(self["items"]) == 0:
             return 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!")
             self.notify.warning("getItemIndexForItemID: cant find itemID for non-class list items!")
             return 0
             return 0
 
 
@@ -309,7 +308,7 @@ class DirectScrolledList(DirectFrame):
     def __incButtonDown(self, event):
     def __incButtonDown(self, event):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
         task = Task(self.__scrollByTask)
         task = Task(self.__scrollByTask)
-        task.setDelay(1.0 / self.scrollSpeed)
+        task.setDelay(1.0 / self.__scrollSpeed)
         task.prevTime = 0.0
         task.prevTime = 0.0
         task.delta = 1
         task.delta = 1
         taskName = self.taskName("scroll")
         taskName = self.taskName("scroll")
@@ -317,13 +316,13 @@ class DirectScrolledList(DirectFrame):
         taskMgr.add(task, taskName)
         taskMgr.add(task, taskName)
         self.scrollBy(task.delta)
         self.scrollBy(task.delta)
         messenger.send('wakeup')
         messenger.send('wakeup')
-        if self.incButtonCallback:
-            self.incButtonCallback()
+        if self.__incButtonCallback:
+            self.__incButtonCallback()
 
 
     def __decButtonDown(self, event):
     def __decButtonDown(self, event):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
         task = Task(self.__scrollByTask)
         task = Task(self.__scrollByTask)
-        task.setDelay(1.0 / self.scrollSpeed)
+        task.setDelay(1.0 / self.__scrollSpeed)
         task.prevTime = 0.0
         task.prevTime = 0.0
         task.delta = -1
         task.delta = -1
         taskName = self.taskName("scroll")
         taskName = self.taskName("scroll")
@@ -331,8 +330,8 @@ class DirectScrolledList(DirectFrame):
         taskMgr.add(task, taskName)
         taskMgr.add(task, taskName)
         self.scrollBy(task.delta)
         self.scrollBy(task.delta)
         messenger.send('wakeup')
         messenger.send('wakeup')
-        if self.decButtonCallback:
-            self.decButtonCallback()
+        if self.__decButtonCallback:
+            self.__decButtonCallback()
 
 
     def __buttonUp(self, event):
     def __buttonUp(self, event):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
@@ -345,7 +344,7 @@ class DirectScrolledList(DirectFrame):
         Add this string and extraArg to the list
         Add this string and extraArg to the list
         """
         """
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
-        if(type(item) == types.InstanceType):
+        if type(item) != type(''):
             # cant add attribs to non-classes (like strings & ints)
             # cant add attribs to non-classes (like strings & ints)
             item.itemID = self.nextItemID
             item.itemID = self.nextItemID
             self.nextItemID += 1
             self.nextItemID += 1
@@ -354,7 +353,7 @@ class DirectScrolledList(DirectFrame):
             item.reparentTo(self.itemFrame)
             item.reparentTo(self.itemFrame)
         if refresh:
         if refresh:
             self.refresh()
             self.refresh()
-        if(type(item) == types.InstanceType):
+        if type(item) != type(''):
             return item.itemID  # to pass to scrollToItemID
             return item.itemID  # to pass to scrollToItemID
 
 
     def removeItem(self, item, refresh=1):
     def removeItem(self, item, refresh=1):
@@ -466,11 +465,11 @@ class DirectScrolledList(DirectFrame):
 
 
     def setIncButtonCallback(self):
     def setIncButtonCallback(self):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
-        self.incButtonCallback = self["incButtonCallback"]
+        self.__incButtonCallback = self["incButtonCallback"]
 
 
     def setDecButtonCallback(self):
     def setDecButtonCallback(self):
         assert self.notify.debugStateCall(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 panda3d.core import *
 from . import DirectGuiGlobals as DGG
 from . import DirectGuiGlobals as DGG
-from direct.showbase.DirectObject import DirectObject
 import sys
 import sys
 
 
 ## These are the styles of text we might commonly see.  They set the
 ## These are the styles of text we might commonly see.  They set the
@@ -17,7 +16,7 @@ ScreenPrompt = 3
 NameConfirm = 4
 NameConfirm = 4
 BlackOnWhite = 5
 BlackOnWhite = 5
 
 
-class OnscreenText(DirectObject, NodePath):
+class OnscreenText(NodePath):
 
 
     def __init__(self, text = '',
     def __init__(self, text = '',
                  style = Plain,
                  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.addInterval(ival, maxDuration - ival.getDuration(), TRACK_START)
         self.popLevel(duration)
         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:
         # Adds a "track list".  This is a list of tuples of the form:
         #
         #
         #   (<delay>, <Interval>,
         #   (<delay>, <Interval>,
@@ -281,19 +281,19 @@ class MetaInterval(CMetaInterval):
         # (TRACK_START).  If the relative code is omitted, the default
         # (TRACK_START).  If the relative code is omitted, the default
         # is TRACK_START.
         # is TRACK_START.
         self.pushLevel(name, relTime, relTo)
         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:
                 else:
                     relTo = TRACK_START
                     relTo = TRACK_START
                 self.addInterval(ival, relTime, relTo)
                 self.addInterval(ival, relTime, relTo)
 
 
             else:
             else:
-                self.notify.error("Not a tuple in Track: %s" % (tuple,))
+                self.notify.error("Not a tuple in Track: %s" % (tupleObj,))
         self.popLevel(duration)
         self.popLevel(duration)
 
 
     def addInterval(self, ival, relTime, relTo):
     def addInterval(self, ival, relTime, relTo):
@@ -593,22 +593,22 @@ class Track(MetaInterval):
         meta.addTrack(self.ivals, self.getName(),
         meta.addTrack(self.ivals, self.getName(),
                       relTime, relTo, self.phonyDuration)
                       relTime, relTo, self.phonyDuration)
 
 
-    def validateComponent(self, tuple):
+    def validateComponent(self, tupleObj):
         # This is called only in debug mode to verify that the
         # This is called only in debug mode to verify that the
         # indicated component added to the MetaInterval is appropriate
         # indicated component added to the MetaInterval is appropriate
         # to this type of MetaInterval.  In most cases except Track,
         # to this type of MetaInterval.  In most cases except Track,
         # this is the same as asking that the component is itself an
         # this is the same as asking that the component is itself an
         # Interval.
         # Interval.
 
 
-        if not (isinstance(tuple, tuple) or \
-                isinstance(tuple, list)):
+        if not (isinstance(tupleObj, tuple) or \
+                isinstance(tupleObj, list)):
             # It's not a tuple.
             # It's not a tuple.
             return 0
             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:
         else:
             relTo = TRACK_START
             relTo = TRACK_START
 
 

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

@@ -20,6 +20,7 @@ def inspect(anObject):
     Inspector = importlib.import_module('direct.tkpanels.Inspector')
     Inspector = importlib.import_module('direct.tkpanels.Inspector')
     return Inspector.inspect(anObject)
     return Inspector.inspect(anObject)
 
 
+import sys
 if sys.version_info >= (3, 0):
 if sys.version_info >= (3, 0):
     import builtins
     import builtins
 else:
 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 vector<Type, allocator> base_class;
   typedef TYPENAME base_class::size_type size_type;
   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(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)) { }
   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
 #endif  // USE_STL_ALLOCATOR

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

@@ -37,14 +37,15 @@ class EXPCL_DTOOL TypeRegistry : public MemoryBase {
 public:
 public:
   // User code shouldn't generally need to call TypeRegistry::register_type()
   // User code shouldn't generally need to call TypeRegistry::register_type()
   // or record_derivation() directly; instead, use the 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);
   bool register_type(TypeHandle &type_handle, const string &name);
+
+PUBLISHED:
   TypeHandle register_dynamic_type(const string &name);
   TypeHandle register_dynamic_type(const string &name);
 
 
   void record_derivation(TypeHandle child, TypeHandle parent);
   void record_derivation(TypeHandle child, TypeHandle parent);
   void record_alternate_name(TypeHandle type, const string &name);
   void record_alternate_name(TypeHandle type, const string &name);
 
 
-PUBLISHED:
   TypeHandle find_type(const string &name) const;
   TypeHandle find_type(const string &name) const;
   TypeHandle find_type_by_id(int id) 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";
       out << "  assert(" << class_ptr << " != NULL);\n";
     } else {
     } else {
       class_ptr = "&Dtool_" + class_name;
       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";
   out << "#if PY_VERSION_HEX >= 0x02050000\n";
   write_function_slot(out, 2, slots, "nb_index");
   write_function_slot(out, 2, slots, "nb_index");
   out << "#endif\n";
   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";
   out << "};\n\n";
 
 
   // NB: it's tempting not to write this table when a class doesn't have them.
   // 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 << "#if PY_VERSION_HEX >= 0x02060000\n";
   out << "    0, // tp_version_tag\n";
   out << "    0, // tp_version_tag\n";
   out << "#endif\n";
   out << "#endif\n";
+  // destructor tp_finalize
+  out << "#if PY_VERSION_HEX >= 0x03040000\n";
+  out << "    0, // tp_finalize\n";
+  out << "#endif\n";
   out << "  },\n";
   out << "  },\n";
 
 
   // It's tempting to initialize the type handle here, but this causes static
   // 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 timeval;
 struct fd_set;
 struct fd_set;
+struct timezone;

+ 0 - 6
makepanda/installer.nsi

@@ -15,7 +15,6 @@
 ;   BUILT         - location of panda install tree.
 ;   BUILT         - location of panda install tree.
 ;   SOURCE        - location of the panda source-tree if available, OR 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")
 ;   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.
 ;   REGVIEW       - either 32 or 64, depending on the build architecture.
 ;
 ;
 
 
@@ -372,11 +371,6 @@ SectionGroup "Python support"
         SetOutPath "$INSTDIR\python"
         SetOutPath "$INSTDIR\python"
         File /r "${BUILT}\python\*"
         File /r "${BUILT}\python\*"
 
 
-        !ifdef PYEXTRAS
-        SetOutPath "$INSTDIR\python\lib"
-        File /nonfatal /r "${PYEXTRAS}\*"
-        !endif
-
         SetDetailsPrint both
         SetDetailsPrint both
         DetailPrint "Adding registry keys for Python..."
         DetailPrint "Adding registry keys for Python..."
         SetDetailsPrint listonly
         SetDetailsPrint listonly

+ 22 - 12
makepanda/makepanda.py

@@ -566,6 +566,10 @@ if (COMPILER == "MSVC"):
                 LibName(pkg, 'dxerrVNUM.lib'.replace("VNUM", vnum))
                 LibName(pkg, 'dxerrVNUM.lib'.replace("VNUM", vnum))
             #LibName(pkg, 'ddraw.lib')
             #LibName(pkg, 'ddraw.lib')
             LibName(pkg, 'dxguid.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")
     IncDirectory("ALWAYS", GetThirdpartyDir() + "extras/include")
     LibName("WINSOCK", "wsock32.lib")
     LibName("WINSOCK", "wsock32.lib")
     LibName("WINSOCK2", "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", "quartz.lib")
     if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbc32.lib")
     if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbc32.lib")
     if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbccp32.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("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("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/vrpn.lib")
     if (PkgSkip("VRPN")==0):     LibName("VRPN",     GetThirdpartyDir() + "vrpn/lib/quat.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("CGGL",     GetThirdpartyDir() + "nvidiacg/lib/cgGL.lib")
     if (PkgSkip("NVIDIACG")==0): LibName("CGDX9",    GetThirdpartyDir() + "nvidiacg/lib/cgD3D9.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("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("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/rfftw.lib")
     if (PkgSkip("FFTW")==0):     LibName("FFTW",     GetThirdpartyDir() + "fftw/lib/fftw.lib")
     if (PkgSkip("FFTW")==0):     LibName("FFTW",     GetThirdpartyDir() + "fftw/lib/fftw.lib")
@@ -708,7 +721,7 @@ if (COMPILER == "MSVC"):
         IncDirectory("SPEEDTREE", SDK["SPEEDTREE"] + "/Include")
         IncDirectory("SPEEDTREE", SDK["SPEEDTREE"] + "/Include")
     if (PkgSkip("BULLET")==0):
     if (PkgSkip("BULLET")==0):
         suffix = '.lib'
         suffix = '.lib'
-        if GetTargetArch() == 'x64':
+        if GetTargetArch() == 'x64' and os.path.isfile(GetThirdpartyDir() + "bullet/lib/BulletCollision_x64.lib"):
             suffix = '_x64.lib'
             suffix = '_x64.lib'
         LibName("BULLET", GetThirdpartyDir() + "bullet/lib/LinearMath" + suffix)
         LibName("BULLET", GetThirdpartyDir() + "bullet/lib/LinearMath" + suffix)
         LibName("BULLET", GetThirdpartyDir() + "bullet/lib/BulletCollision" + 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=OPTS, input=IGATEFILES)
   TargetAdd('libp3putil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3putil', 'SRCDIR:panda/src/putil'])
   TargetAdd('libp3putil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3putil', 'SRCDIR:panda/src/putil'])
   TargetAdd('libp3putil_igate.obj', input='libp3putil.in', opts=["DEPENDENCYONLY"])
   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/
 # DIRECTORY: panda/src/audio/
@@ -4016,8 +4028,7 @@ if (not RUNTIME):
   if PkgSkip("FREETYPE")==0:
   if PkgSkip("FREETYPE")==0:
     TargetAdd('core.pyd', input="libp3pnmtext_igate.obj")
     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='p3pnmimage_pfmFile_ext.obj')
   TargetAdd('core.pyd', input='p3event_pythonTask.obj')
   TargetAdd('core.pyd', input='p3event_pythonTask.obj')
   TargetAdd('core.pyd', input='p3gobj_ext_composite.obj')
   TargetAdd('core.pyd', input='p3gobj_ext_composite.obj')
@@ -6533,7 +6544,6 @@ def MakeInstallerNSIS(file, title, installdir):
         'BUILT'       : panda,
         'BUILT'       : panda,
         'SOURCE'      : psource,
         'SOURCE'      : psource,
         'PYVER'       : SDK["PYTHONVERSION"][6:9],
         'PYVER'       : SDK["PYTHONVERSION"][6:9],
-        'PYEXTRAS'    : os.path.join(os.path.abspath(GetThirdpartyBase()), 'win-extras'),
         'REGVIEW'     : regview,
         'REGVIEW'     : regview,
     }
     }
 
 
@@ -6910,8 +6920,8 @@ def MakeInstallerOSX():
         oscmd("cp -R %s/pandac                dstroot/pythoncode/Developer/Panda3D/pandac" % GetOutputDir())
         oscmd("cp -R %s/pandac                dstroot/pythoncode/Developer/Panda3D/pandac" % GetOutputDir())
         oscmd("cp -R %s/direct                dstroot/pythoncode/Developer/Panda3D/direct" % 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("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"):
         if os.path.isdir(GetOutputDir()+"/Pmw"):
             oscmd("cp -R %s/Pmw               dstroot/pythoncode/Developer/Panda3D/Pmw" % GetOutputDir())
             oscmd("cp -R %s/Pmw               dstroot/pythoncode/Developer/Panda3D/Pmw" % GetOutputDir())
             compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/Pmw")
             compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/Pmw")

+ 49 - 19
makepanda/makepandacore.py

@@ -1122,12 +1122,12 @@ def GetThirdpartyDir():
     target_arch = GetTargetArch()
     target_arch = GetTargetArch()
 
 
     if (target == 'windows'):
     if (target == 'windows'):
+        vc = SDK["VISUALSTUDIO_VERSION"].split('.')[0]
+
         if target_arch == 'x64':
         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:
         else:
-            THIRDPARTYDIR = base + "/win-libs-vc10/"
+            THIRDPARTYDIR = base + "/win-libs-vc" + vc + "/"
 
 
     elif (target == 'darwin'):
     elif (target == 'darwin'):
         # OSX thirdparty binaries are universal, where possible.
         # 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():
     for i, j in PkgConfigGetDefSymbols(pkgname, tool).items():
         DefSymbol(opt, i, j)
         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()
     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:
     for dir in lpath:
         if target == 'darwin' and os.path.isfile(os.path.join(dir, 'lib%s.dylib' % lib)):
         if target == 'darwin' and os.path.isfile(os.path.join(dir, 'lib%s.dylib' % lib)):
             return 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")
         LibName(target_pkg, "-lswresample")
         return
         return
 
 
+    # First check if the package is in the thirdparty directory.
     pkg_dir = os.path.join(GetThirdpartyDir(), pkg.lower())
     pkg_dir = os.path.join(GetThirdpartyDir(), pkg.lower())
     if not custom_loc and os.path.isdir(pkg_dir):
     if not custom_loc and os.path.isdir(pkg_dir):
         if framework and os.path.isdir(os.path.join(pkg_dir, framework + ".framework")):
         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")):
         if os.path.isdir(os.path.join(pkg_dir, "include")):
             IncDirectory(target_pkg, 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"])
             py_lib_dir = os.path.join(pkg_dir, "lib", SDK["PYTHONVERSION"])
             if os.path.isdir(py_lib_dir):
             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))):
         if (tool != None and os.path.isfile(os.path.join(pkg_dir, "bin", tool))):
             tool = os.path.join(pkg_dir, "bin", tool)
             tool = os.path.join(pkg_dir, "bin", tool)
             for i in PkgConfigGetLibs(None, 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():
             for i, j in PkgConfigGetDefSymbols(None, tool).items():
                 DefSymbol(target_pkg, i, j)
                 DefSymbol(target_pkg, i, j)
             return
             return
 
 
+        # Now search for the libraries in the package's lib directories.
         for l in libs:
         for l in libs:
             libname = l
             libname = l
             if l.startswith("lib"):
             if l.startswith("lib"):
                 libname = l[3:]
                 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():
         for d, v in defs.values():
             DefSymbol(target_pkg, d, v)
             DefSymbol(target_pkg, d, v)
@@ -2299,8 +2329,8 @@ def SetupVisualStudioEnviron():
         AddToPathEnv("PATH",    SDK["MSPLATFORM"] + "bin\\" + arch)
         AddToPathEnv("PATH",    SDK["MSPLATFORM"] + "bin\\" + arch)
 
 
         # Windows Kit 10 introduces the "universal CRT".
         # 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 + "shared")
         AddToPathEnv("INCLUDE", inc_dir + "ucrt")
         AddToPathEnv("INCLUDE", inc_dir + "ucrt")
         AddToPathEnv("INCLUDE", inc_dir + "um")
         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) {
 get_contact(int idx) {
 
 
   nassertr(idx >= 0 && idx < (int)_contacts.size(), _empty);
   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:
 PUBLISHED:
   INLINE int get_num_contacts() const;
   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);
   MAKE_SEQ(get_contacts, get_num_contacts, get_contact);
 
 
 public:
 public:

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

@@ -18,7 +18,7 @@ INLINE BulletHeightfieldShape::
 ~BulletHeightfieldShape() {
 ~BulletHeightfieldShape() {
 
 
   delete _shape;
   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);
       _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);
     _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) {
 write_datagram(BamWriter *manager, Datagram &me) {
   AnimChannelMatrix::write_datagram(manager, 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);
   me.add_bool(compress_channels);

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

@@ -146,10 +146,16 @@ void AnimChannelScalarTable::
 write_datagram(BamWriter *manager, Datagram &me) {
 write_datagram(BamWriter *manager, Datagram &me) {
   AnimChannelScalar::write_datagram(manager, 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);
   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();
   AnimChannelScalarTable::register_with_read_factory();
   AnimChannelScalarDynamic::register_with_read_factory();
   AnimChannelScalarDynamic::register_with_read_factory();
   AnimPreloadTable::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::
 INLINE DrawableRegion::
 DrawableRegion() :
 DrawableRegion() :
   _screenshot_buffer_type(RenderBuffer::T_front),
   _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[i] = LColor(0.0f, 0.0f, 0.0f, 0.0f);
   }
   }
   _clear_value[RTP_depth] = LColor(1.0f,1.0f,1.0f,1.0f);
   _clear_value[RTP_depth] = LColor(1.0f,1.0f,1.0f,1.0f);
@@ -35,11 +35,11 @@ INLINE DrawableRegion::
 DrawableRegion(const DrawableRegion &copy) :
 DrawableRegion(const DrawableRegion &copy) :
   _screenshot_buffer_type(copy._screenshot_buffer_type),
   _screenshot_buffer_type(copy._screenshot_buffer_type),
   _draw_buffer_type(copy._draw_buffer_type),
   _draw_buffer_type(copy._draw_buffer_type),
+  _clear_mask(copy._clear_mask),
   _pixel_zoom(copy._pixel_zoom),
   _pixel_zoom(copy._pixel_zoom),
   _pixel_factor(copy._pixel_factor)
   _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];
     _clear_value[i] = copy._clear_value[i];
   }
   }
 }
 }
@@ -51,8 +51,8 @@ INLINE void DrawableRegion::
 operator = (const DrawableRegion &copy) {
 operator = (const DrawableRegion &copy) {
   _screenshot_buffer_type = copy._screenshot_buffer_type;
   _screenshot_buffer_type = copy._screenshot_buffer_type;
   _draw_buffer_type = copy._draw_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];
     _clear_value[i] = copy._clear_value[i];
   }
   }
   _pixel_zoom = copy._pixel_zoom;
   _pixel_zoom = copy._pixel_zoom;
@@ -64,8 +64,8 @@ operator = (const DrawableRegion &copy) {
  */
  */
 INLINE void DrawableRegion::
 INLINE void DrawableRegion::
 copy_clear_settings(const DrawableRegion &copy) {
 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];
     _clear_value[i] = copy._clear_value[i];
   }
   }
   update_pixel_factor();
   update_pixel_factor();

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

@@ -27,8 +27,12 @@ DrawableRegion::
  */
  */
 void DrawableRegion::
 void DrawableRegion::
 set_clear_active(int n, bool clear_active) {
 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();
   update_pixel_factor();
 }
 }
 
 
@@ -37,8 +41,8 @@ set_clear_active(int n, bool clear_active) {
  */
  */
 bool DrawableRegion::
 bool DrawableRegion::
 get_clear_active(int n) const {
 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::
 void DrawableRegion::
 disable_clears() {
 disable_clears() {
-  for (int i = 0; i < RTP_COUNT; ++i) {
-    _clear_active[i] = false;
-  }
+  _clear_mask = 0;
   update_pixel_factor();
   update_pixel_factor();
 }
 }
 
 
@@ -79,12 +81,7 @@ disable_clears() {
  */
  */
 bool DrawableRegion::
 bool DrawableRegion::
 is_any_clear_active() const {
 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:
 protected:
   int _screenshot_buffer_type;
   int _screenshot_buffer_type;
   int _draw_buffer_type;
   int _draw_buffer_type;
+  int _clear_mask;
 
 
 private:
 private:
-  bool    _clear_active[RTP_COUNT];
   LColor  _clear_value[RTP_COUNT];
   LColor  _clear_value[RTP_COUNT];
 
 
   PN_stdfloat _pixel_zoom;
   PN_stdfloat _pixel_zoom;

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

@@ -480,6 +480,13 @@ get_quality(const FrameBufferProperties &reqs) const {
     quality -= 10000000;
     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:
   // Deduct for missing depth, color, alpha, stencil, or accum.  Cost:
   // 1,000,000
   // 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) {
           if (flags & GraphicsPipe::BF_fb_props_optional) {
             display_cat.warning()
             display_cat.warning()
               << "FrameBufferProperties available less than requested.\n";
               << "FrameBufferProperties available less than requested.\n";
+            display_cat.warning(false)
+              << "  requested: " << fb_prop << "\n"
+              << "  got: " << window->get_fb_properties() << "\n";
             return window;
             return window;
           }
           }
           display_cat.error()
           display_cat.error()

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

@@ -2201,7 +2201,10 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
                       bool force) {
                       bool force) {
   _munger = munger;
   _munger = munger;
   _data_reader = data_reader;
   _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);
   INLINE ISocketStream(streambuf *buf);
   virtual ~ISocketStream();
   virtual ~ISocketStream();
 
 
+#if _MSC_VER >= 1800
+  INLINE ISocketStream(const ISocketStream &copy) = delete;
+#endif
+
 PUBLISHED:
 PUBLISHED:
   enum ReadState {
   enum ReadState {
     RS_initial,
     RS_initial,
@@ -155,6 +159,10 @@ class EXPCL_PANDAEXPRESS OSocketStream : public ostream, public SSWriter {
 public:
 public:
   INLINE OSocketStream(streambuf *buf);
   INLINE OSocketStream(streambuf *buf);
 
 
+#if _MSC_VER >= 1800
+  INLINE OSocketStream(const OSocketStream &copy) = delete;
+#endif
+
 PUBLISHED:
 PUBLISHED:
   virtual bool is_closed() = 0;
   virtual bool is_closed() = 0;
   virtual void close() = 0;
   virtual void close() = 0;
@@ -170,6 +178,10 @@ class EXPCL_PANDAEXPRESS SocketStream : public iostream, public SSReader, public
 public:
 public:
   INLINE SocketStream(streambuf *buf);
   INLINE SocketStream(streambuf *buf);
 
 
+#if _MSC_VER >= 1800
+  INLINE SocketStream(const SocketStream &copy) = delete;
+#endif
+
 PUBLISHED:
 PUBLISHED:
   virtual bool is_closed() = 0;
   virtual bool is_closed() = 0;
   virtual void close() = 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();
   PandaSystem *ps = PandaSystem::get_global_ptr();
   ps->add_system("DirectX9");
   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;
     return D3DBLEND_SRCALPHASAT;
 
 
   case ColorBlendAttrib::O_incoming1_color:
   case ColorBlendAttrib::O_incoming1_color:
-    return D3DBLEND_SRCCOLOR2;
+    return (D3DBLEND)16; //D3DBLEND_SRCCOLOR2;
 
 
   case ColorBlendAttrib::O_one_minus_incoming1_color:
   case ColorBlendAttrib::O_one_minus_incoming1_color:
-    return D3DBLEND_INVSRCCOLOR2;
+    return (D3DBLEND)17; //D3DBLEND_INVSRCCOLOR2;
 
 
   case ColorBlendAttrib::O_incoming1_alpha:
   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:
   case ColorBlendAttrib::O_one_minus_incoming1_alpha:
-    // Not supported by DX.
-    return D3DBLEND_INVSRCCOLOR2;
-
+    // Not supported by DX9.
+    return (D3DBLEND)19;
   }
   }
 
 
   dxgsg9_cat.error()
   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_DIMENSIONS_EXT GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
 #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT GL_FRAMEBUFFER_INCOMPLETE_FORMATS
 #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT GL_FRAMEBUFFER_INCOMPLETE_FORMATS
 #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT
 #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_STENCIL_ATTACHMENT_EXT GL_STENCIL_ATTACHMENT
 #define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES
 #define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES
 #define GL_DEPTH_STENCIL_EXT 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_DEPTH24_STENCIL8_EXT GL_DEPTH24_STENCIL8_OES
 #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
 #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
 #define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_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_MAX_3D_TEXTURE_SIZE GL_MAX_3D_TEXTURE_SIZE_OES
 #define GL_SAMPLER_3D GL_SAMPLER_3D_OES
 #define GL_SAMPLER_3D GL_SAMPLER_3D_OES
 #define GL_BGRA GL_BGRA_EXT
 #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_COMPARE_R_TO_TEXTURE_ARB GL_COMPARE_REF_TO_TEXTURE_EXT
 #define GL_SAMPLER_2D_SHADOW GL_SAMPLER_2D_SHADOW_EXT
 #define GL_SAMPLER_2D_SHADOW GL_SAMPLER_2D_SHADOW_EXT
 #define GL_MAX_DRAW_BUFFERS GL_MAX_DRAW_BUFFERS_NV
 #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_SRC1_COLOR GL_SRC1_COLOR_EXT
 #define GL_ONE_MINUS_SRC1_COLOR GL_ONE_MINUS_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_SRC1_ALPHA GL_SRC1_ALPHA_EXT
 #define GL_ONE_MINUS_SRC1_ALPHA GL_ONE_MINUS_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
 // For GLES 3 compat - need a better solution for this
 #define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x1
 #define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x1
 #define GL_ELEMENT_ARRAY_BARRIER_BIT 0x2
 #define GL_ELEMENT_ARRAY_BARRIER_BIT 0x2

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 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 {
           } else {
             if (_fb_properties.get_color_bits() > 16 * 3) {
             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) {
             } else if (_fb_properties.get_color_bits() > 8 * 3) {
               gl_format = GL_RGB16_EXT;
               gl_format = GL_RGB16_EXT;
             } else {
             } else {
@@ -920,11 +920,11 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
             }
             }
           } else {
           } else {
             if (_fb_properties.get_color_bits() > 16 * 3) {
             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) {
             } else if (_fb_properties.get_color_bits() > 8 * 3) {
-              gl_format = GL_RGB16_EXT;
+              gl_format = GL_RGBA16_EXT;
             } else {
             } 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]);
                                         GL_RENDERBUFFER_EXT, _rbm[slot]);
     }
     }
   } else {
   } else {
-    Texture *Tex = attach[slot];
     GLuint gl_format = GL_RGBA;
     GLuint gl_format = GL_RGBA;
 #ifndef OPENGLES
 #ifndef OPENGLES
     switch (slot) {
     switch (slot) {

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

@@ -1955,6 +1955,9 @@ reset() {
 #endif
 #endif
 
 
 #ifdef OPENGLES_1
 #ifdef OPENGLES_1
+  _supports_framebuffer_multisample = false;
+  _supports_framebuffer_blit = false;
+
   if (has_extension("GL_OES_framebuffer_object")) {
   if (has_extension("GL_OES_framebuffer_object")) {
     _supports_framebuffer_object = true;
     _supports_framebuffer_object = true;
     _glIsRenderbuffer = (PFNGLISRENDERBUFFEROESPROC)
     _glIsRenderbuffer = (PFNGLISRENDERBUFFEROESPROC)
@@ -2012,9 +2015,76 @@ reset() {
   _glGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
   _glGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
   _glGenerateMipmap = glGenerateMipmap;
   _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;
     _supports_framebuffer_object = true;
     _glIsRenderbuffer = (PFNGLISRENDERBUFFEREXTPROC)
     _glIsRenderbuffer = (PFNGLISRENDERBUFFEREXTPROC)
       get_extension_func("glIsRenderbufferEXT");
       get_extension_func("glIsRenderbufferEXT");
@@ -2051,14 +2121,25 @@ reset() {
     _glGenerateMipmap = (PFNGLGENERATEMIPMAPEXTPROC)
     _glGenerateMipmap = (PFNGLGENERATEMIPMAPEXTPROC)
       get_extension_func("glGenerateMipmapEXT");
       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 {
   } else {
     _supports_framebuffer_object = false;
     _supports_framebuffer_object = false;
+    _supports_framebuffer_multisample = false;
+    _supports_framebuffer_blit = false;
     _glGenerateMipmap = NULL;
     _glGenerateMipmap = NULL;
   }
   }
 #endif
 #endif
@@ -2087,49 +2168,16 @@ reset() {
   }
   }
 #endif  // !OPENGLES_1
 #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
 #ifndef OPENGLES
   _supports_framebuffer_multisample_coverage_nv = false;
   _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;
     _supports_framebuffer_multisample_coverage_nv = true;
     _glRenderbufferStorageMultisampleCoverage = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC)
     _glRenderbufferStorageMultisampleCoverage = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC)
       get_extension_func("glRenderbufferStorageMultisampleCoverageNV");
       get_extension_func("glRenderbufferStorageMultisampleCoverageNV");
   }
   }
 #endif
 #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)
 #if defined(OPENGLES_1)
   _glDrawBuffers = NULL;
   _glDrawBuffers = NULL;
   _max_color_targets = 1;
   _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);
   _glgsg->_glGetActiveUniform(_glsl_program, i, name_buflen, NULL, &param_size, &param_type, name_buffer);
   GLint p = _glgsg->_glGetUniformLocation(_glsl_program, 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"
   // Some NVidia drivers (361.43 for example) (incorrectly) include "internal"
   // uniforms in the list starting with "_main_" (for example,
   // uniforms in the list starting with "_main_" (for example,
   // "_main_0_gp5fp[0]") we need to skip those, because we don't know anything
   // "_main_0_gp5fp[0]") we need to skip those, because we don't know anything
   // about them
   // about them
   if (strncmp(name_buffer, "_main_", 6) == 0) {
   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;
     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) {
   if (p < 0) {
     // Special meaning, or it's in a uniform block.  Let it go.
     // Special meaning, or it's in a uniform block.  Let it go.
     return;
     return;

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

@@ -2231,7 +2231,8 @@ get_num_primitives() const {
  */
  */
 bool GeomPrimitivePipelineReader::
 bool GeomPrimitivePipelineReader::
 check_valid(const GeomVertexDataPipelineReader *data_reader) const {
 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()) {
       get_max_vertex() >= data_reader->get_num_rows()) {
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG

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

@@ -235,6 +235,15 @@ get_morph_delta(size_t n) const {
   return _morphs[n]._delta;
   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.
  * 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::
 void GeomVertexFormat::Registry::
 make_standard_formats() {
 make_standard_formats() {
+  _empty = register_format(new GeomVertexFormat);
+
   _v3 = register_format(new GeomVertexArrayFormat
   _v3 = register_format(new GeomVertexArrayFormat
                         (InternalName::get_vertex(), 3,
                         (InternalName::get_vertex(), 3,
                          NT_stdfloat, C_point));
                          NT_stdfloat, C_point));
@@ -1011,10 +1013,6 @@ register_format(GeomVertexFormat *format) {
     new_format = (*fi);
     new_format = (*fi);
     if (!new_format->is_registered()) {
     if (!new_format->is_registered()) {
       new_format->do_register();
       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,
   void write_with_data(ostream &out, int indent_level,
                        const GeomVertexData *data) const;
                        const GeomVertexData *data) const;
 
 
+  INLINE static const GeomVertexFormat *get_empty();
+
   // Some standard vertex formats.  No particular requirement to use one of
   // Some standard vertex formats.  No particular requirement to use one of
   // these, but the DirectX renderers can use these formats directly, whereas
   // these, but the DirectX renderers can use these formats directly, whereas
   // any other format will have to be converted first.
   // any other format will have to be converted first.
@@ -227,6 +229,8 @@ private:
     Formats _formats;
     Formats _formats;
     LightReMutex _lock;
     LightReMutex _lock;
 
 
+    CPT(GeomVertexFormat) _empty;
+
     CPT(GeomVertexFormat) _v3;
     CPT(GeomVertexFormat) _v3;
     CPT(GeomVertexFormat) _v3n3;
     CPT(GeomVertexFormat) _v3n3;
     CPT(GeomVertexFormat) _v3t2;
     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;
   bool had_include = false;
   int lineno = 0;
   int lineno = 0;
   while (getline(*source, line)) {
   while (getline(*source, line)) {
-    // We always forward the actual line - the GLSL compiler will silently
-    // ignore #pragma lines anyway.
     ++lineno;
     ++lineno;
-    out << line << "\n";
 
 
     // Check if this line contains a #pragma.
     // Check if this line contains a #pragma.
     char pragma[64];
     char pragma[64];
     if (line.size() < 8 ||
     if (line.size() < 8 ||
         sscanf(line.c_str(), " # pragma %63s", pragma) != 1) {
         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
       // One exception: check for an #endif after an include.  We have to
       // restore the line number in case the include happened under an #if
       // 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) {
     } else if (strcmp(pragma, "optionNV") == 0) {
       // This is processed by NVIDIA drivers.  Don't touch it.
       // This is processed by NVIDIA drivers.  Don't touch it.
+      out << line << "\n";
 
 
     } else {
     } else {
+      // Forward it, the driver will ignore it if it doesn't know it.
+      out << line << "\n";
       shader_cat.warning()
       shader_cat.warning()
         << "Ignoring unknown pragma directive \"" << pragma << "\" at line "
         << "Ignoring unknown pragma directive \"" << pragma << "\" at line "
         << lineno << " of file " << fn << ":\n  " << line << "\n";
         << 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') {
         } else if (format.at(s) == 'R') {
           component = 2;
           component = 2;
         } else if (format.at(s) == 'A') {
         } 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') {
         } else if (format.at(s) == '0') {
           // Ignore.
           // Ignore.
         } else if (format.at(s) == '1') {
         } 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') {
       } else if (format.at(s) == 'R') {
         component = 2;
         component = 2;
       } else if (format.at(s) == 'A') {
       } 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') {
       } else if (format.at(s) == '0') {
         // Ignore.
         // Ignore.
       } else if (format.at(s) == '1') {
       } 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
  * Rather than just returning a pointer to the data, like
  * get_uncompressed_ram_image, this function first processes the data and
  * get_uncompressed_ram_image, this function first processes the data and
  * reorders the components using the specified format string, and places these
  * 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
  * This function is particularly useful to copy an image in-memory to a
  * different library (for example, PIL or wxWidgets) that require a different
  * different library (for example, PIL or wxWidgets) that require a different
  * component order than Panda's internal format, BGRA. Note, however, that
  * 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
  * 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::
 CPTA_uchar Texture::
 get_ram_image_as(const string &requested_format) {
 get_ram_image_as(const string &requested_format) {
@@ -6125,92 +6136,177 @@ get_ram_image_as(const string &requested_format) {
     return CPTA_uchar(data);
     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.
   // 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());
   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.
   // 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 (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 {
         } 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;
     return newdata;
   }
   }
+
+  // The slow and general case.
   for (int p = 0; p < imgsize; ++p) {
   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;
         component = 0;
-      } else if (format.at(s) == 'G') {
+      } else if (format[i] == 'G') {
         component = 1;
         component = 1;
-      } else if (format.at(s) == 'R') {
+      } else if (format[i] == 'R') {
         component = 2;
         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 {
       } 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;
   return newdata;

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

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

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

@@ -1,6 +1,7 @@
 #include "cardMaker.cxx"
 #include "cardMaker.cxx"
 #include "heightfieldTesselator.cxx"
 #include "heightfieldTesselator.cxx"
 #include "geoMipTerrain.cxx"
 #include "geoMipTerrain.cxx"
+#include "shaderTerrainMesh.cxx"
 #include "config_grutil.cxx"
 #include "config_grutil.cxx"
 #include "lineSegs.cxx"
 #include "lineSegs.cxx"
 #include "fisheyeMaker.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 PN_stdfloat get_reference_alpha() const;
   INLINE PandaCompareFunc get_mode() const;
   INLINE PandaCompareFunc get_mode() const;
 
 
+PUBLISHED:
+  MAKE_PROPERTY(reference_alpha, get_reference_alpha);
+  MAKE_PROPERTY(mode, get_mode);
+
 public:
 public:
   virtual void output(ostream &out) const;
   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_type() const;
   INLINE unsigned short get_mode_quality() 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:
 public:
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 

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

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

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

@@ -63,6 +63,9 @@ PUBLISHED:
 
 
   INLINE int get_outputs() const;
   INLINE int get_outputs() const;
 
 
+PUBLISHED:
+  MAKE_PROPERTY(outputs, get_outputs);
+
 public:
 public:
   virtual void output(ostream &out) const;
   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_bool(_active);
   dg.add_uint32(_camera_mask.get_word());
   dg.add_uint32(_camera_mask.get_word());
 
 
-  manager->write_pointer(dg, _initial_state);
-  dg.add_stdfloat(_lod_scale);
+  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::
 int Camera::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = LensNode::complete_pointers(p_list, 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;
   return pi;
 }
 }
 
 

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

@@ -42,6 +42,10 @@ PUBLISHED:
   INLINE Type get_color_type() const;
   INLINE Type get_color_type() const;
   INLINE const LColor &get_color() const;
   INLINE const LColor &get_color() const;
 
 
+PUBLISHED:
+  MAKE_PROPERTY(color_type, get_color_type);
+  MAKE_PROPERTY(color, get_color);
+
 public:
 public:
   virtual void output(ostream &out) const;
   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(_mode);
   dg.add_uint8(_a);
   dg.add_uint8(_a);
   dg.add_uint8(_b);
   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);
   _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_constant_color(Operand operand);
   INLINE static bool involves_color_scale(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:
 public:
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 

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

@@ -43,6 +43,9 @@ PUBLISHED:
   INLINE const LVecBase4 &get_scale() const;
   INLINE const LVecBase4 &get_scale() const;
   CPT(RenderAttrib) set_scale(const LVecBase4 &scale) const;
   CPT(RenderAttrib) set_scale(const LVecBase4 &scale) const;
 
 
+PUBLISHED:
+  MAKE_PROPERTY2(scale, has_scale, get_scale);
+
 public:
 public:
   virtual bool lower_attrib_can_override() const;
   virtual bool lower_attrib_can_override() const;
   virtual void output(ostream &out) 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;
   INLINE unsigned int get_channels() const;
 
 
+PUBLISHED:
+  MAKE_PROPERTY(channels, get_channels);
+
 public:
 public:
   virtual void output(ostream &out) const;
   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 const string &get_bin_name() const;
   INLINE int get_draw_order() const;
   INLINE int get_draw_order() const;
 
 
+PUBLISHED:
+  MAKE_PROPERTY(bin_name, get_bin_name);
+  MAKE_PROPERTY(draw_order, get_draw_order);
+
 public:
 public:
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 

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

@@ -44,6 +44,11 @@ PUBLISHED:
   INLINE bool get_reverse() const;
   INLINE bool get_reverse() const;
   Mode get_effective_mode() 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:
 public:
   virtual void output(ostream &out) const;
   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_min_value() const;
   INLINE PN_stdfloat get_max_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:
 public:
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 

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

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

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

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

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

@@ -34,6 +34,9 @@ PUBLISHED:
   INLINE bool is_off() const;
   INLINE bool is_off() const;
   INLINE Fog *get_fog() const;
   INLINE Fog *get_fog() const;
 
 
+PUBLISHED:
+  MAKE_PROPERTY(fog, get_fog);
+
 public:
 public:
   virtual void output(ostream &out) const;
   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_level(int n) const;
   INLINE PN_stdfloat get_threshold(int n) const;
   INLINE PN_stdfloat get_threshold(int n) const;
 
 
+PUBLISHED:
+  MAKE_PROPERTY(mode, get_mode);
+
 public:
 public:
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 

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

@@ -36,6 +36,9 @@ PUBLISHED:
   INLINE bool is_off() const;
   INLINE bool is_off() const;
   INLINE Material *get_material() const;
   INLINE Material *get_material() const;
 
 
+PUBLISHED:
+  MAKE_PROPERTY(material, get_material);
+
 public:
 public:
   virtual void output(ostream &out) const;
   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 PN_stdfloat get_thickness() const;
   INLINE bool get_perspective() const;
   INLINE bool get_perspective() const;
   INLINE const LColor &get_wireframe_color() const;
   INLINE const LColor &get_wireframe_color() const;
-
   INLINE int get_geom_rendering(int geom_rendering) 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:
 public:
   virtual void output(ostream &out) const;
   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 static CPT(RenderAttrib) make_default();
 
 
   INLINE Mode get_mode() const;
   INLINE Mode get_mode() const;
+  MAKE_PROPERTY(mode, get_mode);
 
 
 public:
 public:
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;

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

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

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

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

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

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

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

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

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

@@ -29,6 +29,7 @@
 #include "selectiveChildNode.h"
 #include "selectiveChildNode.h"
 #include "sequenceNode.h"
 #include "sequenceNode.h"
 #include "shaderGenerator.h"
 #include "shaderGenerator.h"
+#include "sphereLight.h"
 #include "spotlight.h"
 #include "spotlight.h"
 #include "switchNode.h"
 #include "switchNode.h"
 #include "uvScrollNode.h"
 #include "uvScrollNode.h"
@@ -123,6 +124,7 @@ init_libpgraphnodes() {
   SelectiveChildNode::init_type();
   SelectiveChildNode::init_type();
   SequenceNode::init_type();
   SequenceNode::init_type();
   ShaderGenerator::init_type();
   ShaderGenerator::init_type();
+  SphereLight::init_type();
   Spotlight::init_type();
   Spotlight::init_type();
   SwitchNode::init_type();
   SwitchNode::init_type();
   UvScrollNode::init_type();
   UvScrollNode::init_type();
@@ -137,6 +139,7 @@ init_libpgraphnodes() {
   PointLight::register_with_read_factory();
   PointLight::register_with_read_factory();
   SelectiveChildNode::register_with_read_factory();
   SelectiveChildNode::register_with_read_factory();
   SequenceNode::register_with_read_factory();
   SequenceNode::register_with_read_factory();
+  SphereLight::register_with_read_factory();
   Spotlight::register_with_read_factory();
   Spotlight::register_with_read_factory();
   SwitchNode::register_with_read_factory();
   SwitchNode::register_with_read_factory();
   UvScrollNode::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 "fadeLodNodeData.cxx"
 #include "lightLensNode.cxx"
 #include "lightLensNode.cxx"
 #include "lightNode.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 "nodeCullCallbackData.cxx"
 #include "pointLight.cxx"
 #include "pointLight.cxx"
 #include "sceneGraphAnalyzer.cxx"
 #include "sceneGraphAnalyzer.cxx"
 #include "selectiveChildNode.cxx"
 #include "selectiveChildNode.cxx"
 #include "sequenceNode.cxx"
 #include "sequenceNode.cxx"
 #include "shaderGenerator.cxx"
 #include "shaderGenerator.cxx"
+#include "sphereLight.cxx"
 #include "spotlight.cxx"
 #include "spotlight.cxx"
 #include "switchNode.cxx"
 #include "switchNode.cxx"
 #include "uvScrollNode.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();
   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
  * 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_major_ver() const;
   INLINE int get_current_minor_ver() const;
   INLINE int get_current_minor_ver() const;
 
 
+  EXTENSION(PyObject *get_file_version() const);
+
 PUBLISHED:
 PUBLISHED:
   MAKE_PROPERTY(source, get_source, set_source);
   MAKE_PROPERTY(source, get_source, set_source);
   MAKE_PROPERTY(filename, get_filename);
   MAKE_PROPERTY(filename, get_filename);
   MAKE_PROPERTY(loader_options, get_loader_options, set_loader_options);
   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_endian, get_file_endian);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
 
 
@@ -194,7 +197,13 @@ public:
   INLINE streampos get_file_pos();
   INLINE streampos get_file_pos();
 
 
 public:
 public:
+  INLINE static void register_factory(TypeHandle type, WritableFactory::CreateFunc *func,
+                                      void *user_data = NULL);
   INLINE static WritableFactory *get_factory();
   INLINE static WritableFactory *get_factory();
+
+PUBLISHED:
+  EXTENSION(static void register_factory(TypeHandle handle, PyObject *func));
+
 private:
 private:
   INLINE static void create_factory();
   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>
 template<class Type>
 INLINE void Factory<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,
   make_instance_more_general(const string &type_name,
                              const FactoryParams &params = FactoryParams());
                              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"
 #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.
  * 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::
 void FactoryBase::
-register_factory(TypeHandle handle, BaseCreateFunc *func) {
+register_factory(TypeHandle handle, BaseCreateFunc *func, void *user_data) {
   nassertv(handle != TypeHandle::none());
   nassertv(handle != TypeHandle::none());
   nassertv(func != (BaseCreateFunc *)NULL);
   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.
  * not be created.
  */
  */
 TypedObject *FactoryBase::
 TypedObject *FactoryBase::
-make_instance_exact(TypeHandle handle, const FactoryParams &params) {
+make_instance_exact(TypeHandle handle, FactoryParams params) {
   Creators::const_iterator ci = _creators.find(handle);
   Creators::const_iterator ci = _creators.find(handle);
   if (ci == _creators.end()) {
   if (ci == _creators.end()) {
     return NULL;
     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.
  * instance could not be created.
  */
  */
 TypedObject *FactoryBase::
 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
   // First, walk through the established preferred list.  Maybe one of these
   // qualifies.
   // qualifies.
 
 
@@ -272,9 +279,10 @@ make_instance_more_specific(TypeHandle handle, const FactoryParams &params) {
   for (ci = _creators.begin(); ci != _creators.end(); ++ci) {
   for (ci = _creators.begin(); ci != _creators.end(); ++ci) {
     TypeHandle ctype = (*ci).first;
     TypeHandle ctype = (*ci).first;
     if (ctype.is_derived_from(handle)) {
     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) {
       if (object != (TypedObject *)NULL) {
         return object;
         return object;
       }
       }

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

@@ -56,7 +56,7 @@ public:
 
 
   TypeHandle find_registered_type(TypeHandle handle);
   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;
   int get_num_types() const;
   TypeHandle get_type(int n) const;
   TypeHandle get_type(int n) const;
@@ -74,22 +74,18 @@ private:
   void operator = (const FactoryBase &copy);
   void operator = (const FactoryBase &copy);
 
 
   // internal utility functions
   // 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,
   TypedObject *make_instance_more_specific(TypeHandle handle,
-                                           const FactoryParams &params);
+                                           FactoryParams params);
 
 
 private:
 private:
   // internal mechanics and bookkeeping
   // 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;
   Creators _creators;
 
 
   typedef pvector<TypeHandle> Preferred;
   typedef pvector<TypeHandle> Preferred;

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

@@ -13,6 +13,54 @@
 
 
 #include "pnotify.h"
 #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
  * 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"
 #include "factoryParams.h"
 
 
-/**
- *
- */
-FactoryParams::
-FactoryParams() {
-}
-
-/**
- *
- */
-FactoryParams::
-~FactoryParams() {
-}
-
 /**
 /**
  *
  *
  */
  */

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

@@ -35,8 +35,14 @@
  */
  */
 class EXPCL_PANDA_PUTIL FactoryParams {
 class EXPCL_PANDA_PUTIL FactoryParams {
 public:
 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 add_param(FactoryParam *param);
   void clear();
   void clear();
@@ -46,10 +52,15 @@ public:
 
 
   FactoryParam *get_param_of_type(TypeHandle type) const;
   FactoryParam *get_param_of_type(TypeHandle type) const;
 
 
+  INLINE void *get_user_data() const;
+
 private:
 private:
   typedef pvector< PT(TypedReferenceCount) > Params;
   typedef pvector< PT(TypedReferenceCount) > Params;
 
 
   Params _params;
   Params _params;
+  void *_user_data;
+
+  friend class FactoryBase;
 };
 };
 
 
 template<class ParamType>
 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 int complete_pointers(TypedWritable **p_list, BamReader *manager);
   virtual bool require_fully_complete() const;
   virtual bool require_fully_complete() const;
 
 
+PUBLISHED:
   virtual void fillin(DatagramIterator &scan, BamReader *manager);
   virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
   virtual void finalize(BamReader *manager);
   virtual void finalize(BamReader *manager);
 
 
   virtual ReferenceCount *as_reference_count();
   virtual ReferenceCount *as_reference_count();

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

@@ -27,6 +27,14 @@
   #endif
   #endif
 #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_Connection.h"
 #include "vrpn_Tracker.h"
 #include "vrpn_Tracker.h"
 #include "vrpn_Analog.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;
     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()) {
   if (!rebuild_bitplanes()) {
@@ -146,7 +148,7 @@ bind_texture_to_pbuffer() {
   for (size_t i = 0; i != cdata->_textures.size(); ++i) {
   for (size_t i = 0; i != cdata->_textures.size(); ++i) {
     const RenderTexture &rt = cdata->_textures[i];
     const RenderTexture &rt = cdata->_textures[i];
     RenderTexturePlane plane = rt._plane;
     RenderTexturePlane plane = rt._plane;
-    if (plane == RTP_color) {
+    if (plane == RTP_color && rt._rtm_mode == RTM_bind_or_copy) {
       tex_index = i;
       tex_index = i;
       break;
       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


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно