Quellcode durchsuchen

Merge branch 'master' into deploy-ng

rdb vor 8 Jahren
Ursprung
Commit
f16cd29971
100 geänderte Dateien mit 2000 neuen und 1097 gelöschten Zeilen
  1. 35 11
      direct/src/stdpy/thread.py
  2. 33 11
      direct/src/stdpy/threading.py
  3. 43 26
      direct/src/stdpy/threading2.py
  4. 588 632
      dtool/src/cppparser/cppBison.cxx.prebuilt
  5. 4 0
      dtool/src/cppparser/cppBison.yxx
  6. 1 1
      dtool/src/dtoolbase/dtoolbase.h
  7. 4 8
      dtool/src/dtoolbase/dtoolbase_cc.h
  8. 11 16
      dtool/src/dtoolbase/memoryHook.cxx
  9. 1 3
      dtool/src/dtoolbase/memoryHook.h
  10. 0 2
      dtool/src/dtoolbase/typeRegistryNode.cxx
  11. 0 2
      dtool/src/dtoolbase/typeRegistryNode.h
  12. 16 0
      dtool/src/dtoolutil/executionEnvironment.cxx
  13. 5 0
      dtool/src/dtoolutil/executionEnvironment.h
  14. 11 4
      dtool/src/dtoolutil/filename.cxx
  15. 5 5
      dtool/src/dtoolutil/load_dso.cxx
  16. 15 0
      dtool/src/dtoolutil/pandaSystem.h
  17. 1 0
      dtool/src/dtoolutil/textEncoder.h
  18. 2 1
      dtool/src/interrogate/interfaceMaker.cxx
  19. 1 0
      dtool/src/interrogate/interfaceMaker.h
  20. 127 41
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  21. 138 3
      dtool/src/interrogatedb/py_panda.cxx
  22. 3 0
      dtool/src/interrogatedb/py_panda.h
  23. 4 0
      dtool/src/pystub/pystub.cxx
  24. 1 1
      makepanda/config.in
  25. 23 6
      makepanda/makepanda.py
  26. 12 0
      makepanda/makepandacore.py
  27. 2 2
      panda/src/audio/audioManager.cxx
  28. 3 2
      panda/src/audio/audioManager.h
  29. 2 2
      panda/src/audiotraits/fmodAudioManager.cxx
  30. 2 2
      panda/src/audiotraits/fmodAudioManager.h
  31. 4 4
      panda/src/bullet/bulletHeightfieldShape.I
  32. 5 5
      panda/src/bullet/bulletHeightfieldShape.cxx
  33. 1 1
      panda/src/bullet/bulletHeightfieldShape.h
  34. 31 7
      panda/src/bullet/bulletTriangleMesh.cxx
  35. 15 1
      panda/src/cocoadisplay/cocoaGraphicsWindow.mm
  36. 1 0
      panda/src/collide/collisionNode.h
  37. 1 1
      panda/src/cull/cullBinBackToFront.cxx
  38. 1 1
      panda/src/cull/cullBinFixed.cxx
  39. 1 1
      panda/src/cull/cullBinFrontToBack.cxx
  40. 1 1
      panda/src/cull/cullBinStateSorted.cxx
  41. 1 1
      panda/src/cull/cullBinUnsorted.cxx
  42. 6 2
      panda/src/display/frameBufferProperties.cxx
  43. 87 7
      panda/src/display/graphicsStateGuardian.cxx
  44. 1 3
      panda/src/display/graphicsStateGuardian.h
  45. 6 5
      panda/src/display/standardMunger.cxx
  46. 2 0
      panda/src/display/windowProperties.h
  47. 1 3
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  48. 0 1
      panda/src/dxgsg9/dxGraphicsStateGuardian9.h
  49. 5 5
      panda/src/egg/eggLine.h
  50. 4 4
      panda/src/egg/eggNurbsCurve.h
  51. 5 5
      panda/src/egg/eggNurbsSurface.h
  52. 4 4
      panda/src/egg/eggPatch.h
  53. 5 5
      panda/src/egg/eggPoint.h
  54. 5 5
      panda/src/egg/eggPolygon.h
  55. 7 7
      panda/src/egg/eggTriangleFan.h
  56. 6 6
      panda/src/egg/eggTriangleStrip.h
  57. 3 0
      panda/src/express/memoryInfo.h
  58. 100 4
      panda/src/express/memoryUsage.I
  59. 157 23
      panda/src/express/memoryUsage.cxx
  60. 25 10
      panda/src/express/memoryUsage.h
  61. 6 5
      panda/src/express/memoryUsagePointerCounts.cxx
  62. 0 4
      panda/src/express/memoryUsagePointerCounts.h
  63. 33 11
      panda/src/express/memoryUsagePointers.cxx
  64. 2 5
      panda/src/express/memoryUsagePointers.h
  65. 1 0
      panda/src/express/multifile.h
  66. 98 0
      panda/src/express/ordered_vector.I
  67. 12 0
      panda/src/express/ordered_vector.h
  68. 2 2
      panda/src/express/pointerTo.h
  69. 2 3
      panda/src/express/pointerToBase.I
  70. 1 3
      panda/src/express/pointerToBase.h
  71. 2 3
      panda/src/express/threadSafePointerToBase.I
  72. 0 2
      panda/src/express/threadSafePointerToBase.h
  73. 9 0
      panda/src/express/virtualFileSimple.cxx
  74. 18 0
      panda/src/express/virtualFileSystem.cxx
  75. 0 34
      panda/src/glstuff/glCgShaderContext_src.I
  76. 14 0
      panda/src/glstuff/glCgShaderContext_src.cxx
  77. 13 13
      panda/src/glstuff/glCgShaderContext_src.h
  78. 4 5
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  79. 0 1
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  80. 0 35
      panda/src/glstuff/glShaderContext_src.I
  81. 19 5
      panda/src/glstuff/glShaderContext_src.cxx
  82. 19 15
      panda/src/glstuff/glShaderContext_src.h
  83. 6 6
      panda/src/gobj/geom.cxx
  84. 1 2
      panda/src/gobj/geom.h
  85. 0 2
      panda/src/gobj/internalName.h
  86. 8 0
      panda/src/gobj/material.I
  87. 2 2
      panda/src/gobj/material.h
  88. 109 8
      panda/src/gobj/shader.cxx
  89. 11 0
      panda/src/gobj/shader.h
  90. 2 0
      panda/src/gobj/textureStage.h
  91. 1 0
      panda/src/gobj/textureStagePool.h
  92. 1 0
      panda/src/gobj/vertexDataPage.h
  93. 0 1
      panda/src/gsgbase/graphicsStateGuardianBase.h
  94. 0 2
      panda/src/mathutil/geometricBoundingVolume.h
  95. 17 0
      panda/src/parametrics/ropeNode.h
  96. 0 16
      panda/src/pgraph/alphaTestAttrib.cxx
  97. 1 1
      panda/src/pgraph/alphaTestAttrib.h
  98. 1 0
      panda/src/pgraph/antialiasAttrib.h
  99. 1 0
      panda/src/pgraph/audioVolumeAttrib.h
  100. 0 8
      panda/src/pgraph/auxBitplaneAttrib.cxx

+ 35 - 11
direct/src/stdpy/thread.py

@@ -11,18 +11,32 @@ __all__ = [
     'interrupt_main',
     'exit', 'allocate_lock', 'get_ident',
     'stack_size',
+    'force_yield', 'consider_yield',
     'forceYield', 'considerYield',
+    'TIMEOUT_MAX'
     ]
 
 from panda3d import core
+import sys
+
+if sys.platform == "win32":
+    TIMEOUT_MAX = float(0xffffffff // 1000)
+else:
+    TIMEOUT_MAX = float(0x7fffffffffffffff // 1000000000)
 
 # These methods are defined in Panda, and are particularly useful if
 # you may be running in Panda's SIMPLE_THREADS compilation mode.
-forceYield = core.Thread.forceYield
-considerYield = core.Thread.considerYield
+force_yield = core.Thread.force_yield
+consider_yield = core.Thread.consider_yield
 
-class error(Exception):
-    pass
+forceYield = force_yield
+considerYield = consider_yield
+
+if sys.version_info >= (3, 3):
+    error = RuntimeError
+else:
+    class error(Exception):
+        pass
 
 class LockType:
     """ Implements a mutex lock.  Instead of directly subclassing
@@ -36,13 +50,18 @@ class LockType:
         self.__cvar = core.ConditionVar(self.__lock)
         self.__locked = False
 
-    def acquire(self, waitflag = 1):
+    def acquire(self, waitflag = 1, timeout = -1):
         self.__lock.acquire()
         try:
             if self.__locked and not waitflag:
                 return False
-            while self.__locked:
-                self.__cvar.wait()
+
+            if timeout >= 0:
+                while self.__locked:
+                    self.__cvar.wait(timeout)
+            else:
+                while self.__locked:
+                    self.__cvar.wait()
 
             self.__locked = True
             return True
@@ -202,12 +221,17 @@ def _get_thread_locals(thread, i):
 def _remove_thread_id(threadId):
     """ Removes the thread with the indicated ID from the thread list. """
 
+    # On interpreter shutdown, Python may set module globals to None.
+    if _threadsLock is None or _threads is None:
+        return
+
     _threadsLock.acquire()
     try:
-        thread, locals, wrapper = _threads[threadId]
-        assert thread.getPythonIndex() == threadId
-        del _threads[threadId]
-        thread.setPythonIndex(-1)
+        if threadId in _threads:
+            thread, locals, wrapper = _threads[threadId]
+            assert thread.getPythonIndex() == threadId
+            del _threads[threadId]
+            thread.setPythonIndex(-1)
 
     finally:
         _threadsLock.release()

+ 33 - 11
direct/src/stdpy/threading.py

@@ -35,11 +35,15 @@ __all__ = [
     'Event',
     'Timer',
     'local',
-    'current_thread', 'currentThread',
-    'enumerate', 'active_count', 'activeCount',
+    'current_thread',
+    'main_thread',
+    'enumerate', 'active_count',
     'settrace', 'setprofile', 'stack_size',
+    'TIMEOUT_MAX',
     ]
 
+TIMEOUT_MAX = _thread.TIMEOUT_MAX
+
 local = _thread._local
 _newname = _thread._newname
 
@@ -98,7 +102,15 @@ class Thread(ThreadBase):
         self.__dict__['daemon'] = current.daemon
         self.__dict__['name'] = name
 
-        self.__thread = core.PythonThread(self.run, None, name, name)
+        def call_run():
+            # As soon as the thread is done, break the circular reference.
+            try:
+                self.run()
+            finally:
+                self.__thread = None
+                _thread._remove_thread_id(self.ident)
+
+        self.__thread = core.PythonThread(call_run, None, name, name)
         threadId = _thread._add_thread(self.__thread, weakref.proxy(self))
         self.__dict__['ident'] = threadId
 
@@ -109,16 +121,17 @@ class Thread(ThreadBase):
             _thread._remove_thread_id(self.ident)
 
     def is_alive(self):
-        return self.__thread.isStarted()
+        thread = self.__thread
+        return thread is not None and thread.is_started()
 
-    def isAlive(self):
-        return self.__thread.isStarted()
+    isAlive = is_alive
 
     def start(self):
-        if self.__thread.isStarted():
+        thread = self.__thread
+        if thread is None or thread.is_started():
             raise RuntimeError
 
-        if not self.__thread.start(core.TPNormal, True):
+        if not thread.start(core.TPNormal, True):
             raise RuntimeError
 
     def run(self):
@@ -132,8 +145,12 @@ class Thread(ThreadBase):
     def join(self, timeout = None):
         # We don't support a timed join here, sorry.
         assert timeout is None
-        self.__thread.join()
-        self.__thread = None
+        thread = self.__thread
+        if thread is not None:
+            thread.join()
+            # Clear the circular reference.
+            self.__thread = None
+            _thread._remove_thread_id(self.ident)
 
     def setName(self, name):
         self.__dict__['name'] = name
@@ -379,6 +396,10 @@ def current_thread():
     t = core.Thread.getCurrentThread()
     return _thread._get_thread_wrapper(t, _create_thread_wrapper)
 
+def main_thread():
+    t = core.Thread.getMainThread()
+    return _thread._get_thread_wrapper(t, _create_thread_wrapper)
+
 currentThread = current_thread
 
 def enumerate():
@@ -386,7 +407,7 @@ def enumerate():
     _thread._threadsLock.acquire()
     try:
         for thread, locals, wrapper in list(_thread._threads.values()):
-            if wrapper and thread.isStarted():
+            if wrapper and wrapper.is_alive():
                 tlist.append(wrapper)
         return tlist
     finally:
@@ -394,6 +415,7 @@ def enumerate():
 
 def active_count():
     return len(enumerate())
+
 activeCount = active_count
 
 _settrace_func = None

+ 43 - 26
direct/src/stdpy/threading2.py

@@ -15,7 +15,7 @@ implementation. """
 
 import sys as _sys
 
-from direct.stdpy import thread
+from direct.stdpy import thread as _thread
 from direct.stdpy.thread import stack_size, _newname, _local as local
 from panda3d import core
 _sleep = core.Thread.sleep
@@ -23,16 +23,19 @@ _sleep = core.Thread.sleep
 from time import time as _time
 from traceback import format_exc as _format_exc
 
-# Rename some stuff so "from threading import *" is safe
-__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event',
-           'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
-           'Timer', 'setprofile', 'settrace', 'local', 'stack_size']
+__all__ = ['get_ident', 'active_count', 'Condition', 'current_thread',
+           'enumerate', 'main_thread', 'TIMEOUT_MAX',
+           'Event', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
+           'Timer', 'ThreadError',
+           'setprofile', 'settrace', 'local', 'stack_size']
 
-_start_new_thread = thread.start_new_thread
-_allocate_lock = thread.allocate_lock
-_get_ident = thread.get_ident
-ThreadError = thread.error
-del thread
+# Rename some stuff so "from threading import *" is safe
+_start_new_thread = _thread.start_new_thread
+_allocate_lock = _thread.allocate_lock
+get_ident = _thread.get_ident
+ThreadError = _thread.error
+TIMEOUT_MAX = _thread.TIMEOUT_MAX
+del _thread
 
 
 # Debug support (adapted from ihooks.py).
@@ -446,7 +449,7 @@ class Thread(_Verbose):
         try:
             self.__started = True
             _active_limbo_lock.acquire()
-            _active[_get_ident()] = self
+            _active[get_ident()] = self
             del _limbo[self]
             _active_limbo_lock.release()
             if __debug__:
@@ -537,7 +540,7 @@ class Thread(_Verbose):
         _active_limbo_lock.acquire()
         try:
             try:
-                del _active[_get_ident()]
+                del _active[get_ident()]
             except KeyError:
                 if 'dummy_threading' not in _sys.modules:
                     raise
@@ -581,10 +584,12 @@ class Thread(_Verbose):
         assert self.__initialized, "Thread.__init__() not called"
         self.__name = str(name)
 
-    def isAlive(self):
+    def is_alive(self):
         assert self.__initialized, "Thread.__init__() not called"
         return self.__started and not self.__stopped
 
+    isAlive = is_alive
+
     def isDaemon(self):
         assert self.__initialized, "Thread.__init__() not called"
         return self.__daemonic
@@ -634,7 +639,7 @@ class _MainThread(Thread):
         Thread.__init__(self, name="MainThread")
         self._Thread__started = True
         _active_limbo_lock.acquire()
-        _active[_get_ident()] = self
+        _active[get_ident()] = self
         _active_limbo_lock.release()
 
     def _set_daemon(self):
@@ -653,12 +658,6 @@ class _MainThread(Thread):
             self._note("%s: exiting", self)
         self._Thread__delete()
 
-def _pickSomeNonDaemonThread():
-    for t in enumerate():
-        if not t.isDaemon() and t.isAlive():
-            return t
-    return None
-
 
 # Dummy thread class to represent threads not started here.
 # These aren't garbage collected when they die, nor can they be waited for.
@@ -680,7 +679,7 @@ class _DummyThread(Thread):
 
         self._Thread__started = True
         _active_limbo_lock.acquire()
-        _active[_get_ident()] = self
+        _active[get_ident()] = self
         _active_limbo_lock.release()
 
     def _set_daemon(self):
@@ -692,19 +691,23 @@ class _DummyThread(Thread):
 
 # Global API functions
 
-def currentThread():
+def current_thread():
     try:
-        return _active[_get_ident()]
+        return _active[get_ident()]
     except KeyError:
-        ##print "currentThread(): no current thread for", _get_ident()
+        ##print "current_thread(): no current thread for", get_ident()
         return _DummyThread()
 
-def activeCount():
+currentThread = current_thread
+
+def active_count():
     _active_limbo_lock.acquire()
     count = len(_active) + len(_limbo)
     _active_limbo_lock.release()
     return count
 
+activeCount = active_count
+
 def enumerate():
     _active_limbo_lock.acquire()
     active = list(_active.values()) + list(_limbo.values())
@@ -717,7 +720,21 @@ def enumerate():
 # and make it available for the interpreter
 # (Py_Main) as threading._shutdown.
 
-_shutdown = _MainThread()._exitfunc
+_main_thread = _MainThread()
+_shutdown = _main_thread._exitfunc
+
+def _pickSomeNonDaemonThread():
+    for t in enumerate():
+        if not t.isDaemon() and t.isAlive():
+            return t
+    return None
+
+def main_thread():
+    """Return the main thread object.
+    In normal conditions, the main thread is the thread from which the
+    Python interpreter was started.
+    """
+    return _main_thread
 
 # get thread-local implementation, either from the thread
 # module, or from the python fallback

Datei-Diff unterdrückt, da er zu groß ist
+ 588 - 632
dtool/src/cppparser/cppBison.cxx.prebuilt


+ 4 - 0
dtool/src/cppparser/cppBison.yxx

@@ -4051,6 +4051,10 @@ name:
         | KW_STATIC
 {
   $$ = new CPPIdentifier("static", @1);
+}
+        | KW_DEFAULT
+{
+  $$ = new CPPIdentifier("default", @1);
 }
         ;
 

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

@@ -332,7 +332,7 @@ typedef struct _object PyObject;
 
 #ifdef __WORDSIZE
 #define NATIVE_WORDSIZE __WORDSIZE
-#elif defined(_LP64)
+#elif defined(_LP64) || defined(_WIN64)
 #define NATIVE_WORDSIZE 64
 #else
 #define NATIVE_WORDSIZE 32

+ 4 - 8
dtool/src/dtoolbase/dtoolbase_cc.h

@@ -34,9 +34,9 @@ using namespace std;
 #define ALWAYS_INLINE inline
 #define TYPENAME typename
 #define CONSTEXPR constexpr
+#define ALWAYS_INLINE_CONSTEXPR constexpr
 #define NOEXCEPT noexcept
 #define FINAL final
-#define OVERRIDE override
 #define MOVE(x) x
 #define DEFAULT_CTOR = default
 #define DEFAULT_DTOR = default
@@ -166,7 +166,6 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 #  endif
 #  if __has_extension(cxx_override_control) && (__cplusplus >= 201103L)
 #    define FINAL final
-#    define OVERRIDE override
 #  endif
 #  if __has_extension(cxx_defaulted_functions)
 #     define DEFAULT_CTOR = default
@@ -197,7 +196,6 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 // Starting at GCC 4.7
 #  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
 #    define FINAL final
-#    define OVERRIDE override
 #  endif
 
 // GCC defines several macros which we can query.  List of all supported
@@ -211,11 +209,9 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 #  define NOEXCEPT noexcept
 #  define USE_MOVE_SEMANTICS
 #  define FINAL final
-#  define OVERRIDE override
 #  define MOVE(x) move(x)
 #elif defined(_MSC_VER) && _MSC_VER >= 1600 // Visual Studio 2010
 #  define NOEXCEPT throw()
-#  define OVERRIDE override
 #  define USE_MOVE_SEMANTICS
 #  define FINAL sealed
 #  define MOVE(x) move(x)
@@ -231,6 +227,9 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 // Fallbacks if features are not supported
 #ifndef CONSTEXPR
 #  define CONSTEXPR INLINE
+#  define ALWAYS_INLINE_CONSTEXPR ALWAYS_INLINE
+#else
+#  define ALWAYS_INLINE_CONSTEXPR ALWAYS_INLINE CONSTEXPR
 #endif
 #ifndef NOEXCEPT
 #  define NOEXCEPT
@@ -241,9 +240,6 @@ template<class T> typename remove_reference<T>::type &&move(T &&t) {
 #ifndef FINAL
 #  define FINAL
 #endif
-#ifndef OVERRIDE
-#  define OVERRIDE
-#endif
 #ifndef DEFAULT_CTOR
 #  define DEFAULT_CTOR {}
 #endif

+ 11 - 16
dtool/src/dtoolbase/memoryHook.cxx

@@ -202,13 +202,11 @@ MemoryHook() {
 
 #endif  // WIN32
 
-#ifdef DO_MEMORY_USAGE
   _total_heap_single_size = 0;
   _total_heap_array_size = 0;
   _requested_heap_size = 0;
   _total_mmap_size = 0;
   _max_heap_size = ~(size_t)0;
-#endif
 }
 
 /**
@@ -216,19 +214,16 @@ MemoryHook() {
  */
 MemoryHook::
 MemoryHook(const MemoryHook &copy) :
-  _page_size(copy._page_size)
-{
-#ifdef DO_MEMORY_USAGE
-  _total_heap_single_size = copy._total_heap_single_size;
-  _total_heap_array_size = copy._total_heap_array_size;
-  _requested_heap_size = copy._requested_heap_size;
-  _total_mmap_size = copy._total_mmap_size;
-  _max_heap_size = copy._max_heap_size;
-#endif
-
-  ((MutexImpl &)copy._lock).acquire();
+  _page_size(copy._page_size),
+  _total_heap_single_size(copy._total_heap_single_size),
+  _total_heap_array_size(copy._total_heap_array_size),
+  _requested_heap_size(copy._requested_heap_size),
+  _total_mmap_size(copy._total_mmap_size),
+  _max_heap_size(copy._max_heap_size) {
+
+  copy._lock.acquire();
   _deleted_chains = copy._deleted_chains;
-  ((MutexImpl &)copy._lock).release();
+  copy._lock.release();
 }
 
 /**
@@ -631,7 +626,6 @@ alloc_fail(size_t attempted_size) {
   abort();
 }
 
-#ifdef DO_MEMORY_USAGE
 /**
  * This callback method is called whenever the total allocated heap size
  * exceeds _max_heap_size.  It's mainly intended for reporting memory leaks,
@@ -642,6 +636,7 @@ alloc_fail(size_t attempted_size) {
  */
 void MemoryHook::
 overflow_heap_size() {
+#ifdef DO_MEMORY_USAGE
   _max_heap_size = ~(size_t)0;
-}
 #endif  // DO_MEMORY_USAGE
+}

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

@@ -67,7 +67,6 @@ public:
 
   INLINE static size_t get_ptr_size(void *ptr);
 
-#ifdef DO_MEMORY_USAGE
 protected:
   TVOLATILE AtomicAdjust::Integer _total_heap_single_size;
   TVOLATILE AtomicAdjust::Integer _total_heap_array_size;
@@ -79,7 +78,6 @@ protected:
   size_t _max_heap_size;
 
   virtual void overflow_heap_size();
-#endif  // DO_MEMORY_USAGE
 
 private:
   size_t _page_size;
@@ -87,7 +85,7 @@ private:
   typedef map<size_t, DeletedBufferChain *> DeletedChains;
   DeletedChains _deleted_chains;
 
-  MutexImpl _lock;
+  mutable MutexImpl _lock;
 };
 
 #include "memoryHook.I"

+ 0 - 2
dtool/src/dtoolbase/typeRegistryNode.cxx

@@ -26,9 +26,7 @@ TypeRegistryNode(TypeHandle handle, const string &name, TypeHandle &ref) :
   _handle(handle), _name(name), _ref(ref)
 {
   clear_subtree();
-#ifdef DO_MEMORY_USAGE
   memset(_memory_usage, 0, sizeof(_memory_usage));
-#endif
 }
 
 /**

+ 0 - 2
dtool/src/dtoolbase/typeRegistryNode.h

@@ -47,9 +47,7 @@ public:
   Classes _parent_classes;
   Classes _child_classes;
 
-#ifdef DO_MEMORY_USAGE
   AtomicAdjust::Integer _memory_usage[TypeHandle::MC_limit];
-#endif
 
   static bool _paranoid_inheritance;
 

+ 16 - 0
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -341,6 +341,22 @@ ns_get_environment_variable(const string &var) const {
     }
   }
 
+#elif !defined(__APPLE__)
+  // Similarly, we define fallbacks on POSIX systems for the variables defined
+  // in the XDG Base Directory specification, so that they can be safely used
+  // in Config.prc files.
+  if (var == "XDG_CONFIG_HOME") {
+    Filename home_dir = Filename::get_home_directory();
+    return home_dir.get_fullpath() + "/.config";
+
+  } else if (var == "XDG_CACHE_HOME") {
+    Filename home_dir = Filename::get_home_directory();
+    return home_dir.get_fullpath() + "/.cache";
+
+  } else if (var == "XDG_DATA_HOME") {
+    Filename home_dir = Filename::get_home_directory();
+    return home_dir.get_fullpath() + "/.local/share";
+  }
 #endif // _WIN32
 
   return string();

+ 5 - 0
dtool/src/dtoolutil/executionEnvironment.h

@@ -51,6 +51,11 @@ PUBLISHED:
 
   static Filename get_cwd();
 
+  MAKE_SEQ_PROPERTY(args, get_num_args, get_arg);
+  MAKE_PROPERTY(binary_name, get_binary_name, set_binary_name);
+  MAKE_PROPERTY(dtool_name, get_dtool_name, set_dtool_name);
+  MAKE_PROPERTY(cwd, get_cwd);
+
 private:
   bool ns_has_environment_variable(const string &var) const;
   string ns_get_environment_variable(const string &var) const;

+ 11 - 4
dtool/src/dtoolutil/filename.cxx

@@ -600,8 +600,14 @@ get_user_appdata_directory() {
     user_appdata_directory.set_basename("files");
 
 #else
-    // Posix case.
-    user_appdata_directory = get_home_directory();
+    // Posix case.  We follow the XDG base directory spec.
+    struct stat st;
+    const char *datadir = getenv("XDG_DATA_HOME");
+    if (datadir != nullptr && stat(datadir, &st) == 0 && S_ISDIR(st.st_mode)) {
+      user_appdata_directory = datadir;
+    } else {
+      user_appdata_directory = Filename(get_home_directory(), ".local/share");
+    }
 
 #endif  // WIN32
 
@@ -649,9 +655,10 @@ get_common_appdata_directory() {
     common_appdata_directory.set_dirname(_internal_data_dir);
     common_appdata_directory.set_basename("files");
 
+#elif defined(__FreeBSD__)
+    common_appdata_directory = "/usr/local/share";
 #else
-    // Posix case.
-    common_appdata_directory = "/var";
+    common_appdata_directory = "/usr/share";
 #endif  // WIN32
 
     if (common_appdata_directory.empty()) {

+ 5 - 5
dtool/src/dtoolutil/load_dso.cxx

@@ -47,20 +47,20 @@ load_dso(const DSearchPath &path, const Filename &filename) {
   if (!abspath.is_regular_file()) {
     return NULL;
   }
-  string os_specific = abspath.to_os_specific();
+  wstring os_specific_w = abspath.to_os_specific_w();
 
   // Try using LoadLibraryEx, if possible.
-  typedef HMODULE (WINAPI *tLoadLibraryEx)(LPCTSTR, HANDLE, DWORD);
+  typedef HMODULE (WINAPI *tLoadLibraryEx)(LPCWSTR, HANDLE, DWORD);
   tLoadLibraryEx pLoadLibraryEx;
   HINSTANCE hLib = LoadLibrary("kernel32.dll");
   if (hLib) {
-    pLoadLibraryEx = (tLoadLibraryEx)GetProcAddress(hLib, "LoadLibraryExA");
+    pLoadLibraryEx = (tLoadLibraryEx)GetProcAddress(hLib, "LoadLibraryExW");
     if (pLoadLibraryEx) {
-      return pLoadLibraryEx(os_specific.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+      return pLoadLibraryEx(os_specific_w.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
     }
   }
 
-  return LoadLibrary(os_specific.c_str());
+  return LoadLibraryW(os_specific_w.c_str());
 }
 
 bool

+ 15 - 0
dtool/src/dtoolutil/pandaSystem.h

@@ -48,6 +48,21 @@ PUBLISHED:
 
   static string get_platform();
 
+  MAKE_PROPERTY(version_string, get_version_string);
+  MAKE_PROPERTY(major_version, get_major_version);
+  MAKE_PROPERTY(minor_version, get_minor_version);
+  MAKE_PROPERTY(sequence_version, get_sequence_version);
+  MAKE_PROPERTY(official_version, is_official_version);
+
+  MAKE_PROPERTY(memory_alignment, get_memory_alignment);
+
+  MAKE_PROPERTY(distributor, get_distributor);
+  MAKE_PROPERTY(compiler, get_compiler);
+  MAKE_PROPERTY(build_date, get_build_date);
+  MAKE_PROPERTY(git_commit, get_git_commit);
+
+  MAKE_PROPERTY(platform, get_platform);
+
   bool has_system(const string &system) const;
   size_t get_num_systems() const;
   string get_system(size_t n) const;

+ 1 - 0
dtool/src/dtoolutil/textEncoder.h

@@ -46,6 +46,7 @@ PUBLISHED:
 
   INLINE static void set_default_encoding(Encoding encoding);
   INLINE static Encoding get_default_encoding();
+  MAKE_PROPERTY(default_encoding, get_default_encoding, set_default_encoding);
 
   INLINE void set_text(const string &text);
   INLINE void set_text(const string &text, Encoding encoding);

+ 2 - 1
dtool/src/interrogate/interfaceMaker.cxx

@@ -91,7 +91,8 @@ Property(const InterrogateElement &ielement) :
   _setter(NULL),
   _has_function(NULL),
   _clear_function(NULL),
-  _deleter(NULL)
+  _deleter(NULL),
+  _has_this(false)
 {
 }
 

+ 1 - 0
dtool/src/interrogate/interfaceMaker.h

@@ -132,6 +132,7 @@ public:
     Function *_has_function;
     Function *_clear_function;
     Function *_deleter;
+    bool _has_this;
   };
   typedef vector<Property *> Properties;
 

+ 127 - 41
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -2623,7 +2623,8 @@ write_module_class(ostream &out, Object *obj) {
     for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
       Property *property = (*pit);
       const InterrogateElement &ielem = property->_ielement;
-      if (property->_getter == NULL || !is_function_legal(property->_getter)) {
+      if (!property->_has_this ||
+          property->_getter == NULL || !is_function_legal(property->_getter)) {
         continue;
       }
 
@@ -3169,6 +3170,45 @@ write_module_class(ostream &out, Object *obj) {
     }
   }
 
+  // Also add the static properties, which can't be added via getset.
+  Properties::const_iterator pit;
+  for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
+    Property *property = (*pit);
+    const InterrogateElement &ielem = property->_ielement;
+    if (property->_has_this ||
+        property->_getter == NULL || !is_function_legal(property->_getter)) {
+      continue;
+    }
+
+    string name1 = methodNameFromCppName(ielem.get_name(), "", false);
+    // string name2 = methodNameFromCppName(ielem.get_name(), "", true);
+
+    string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
+    string setter = "NULL";
+    if (property->_length_function == NULL &&
+        property->_setter != NULL && is_function_legal(property->_setter)) {
+      setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
+    }
+
+    out << "    static const PyGetSetDef def_" << name1 << " = {(char *)\"" << name1 << "\", " << getter << ", " << setter;
+
+    if (ielem.has_comment()) {
+      out << ", (char *)\n";
+      output_quoted(out, 4, ielem.get_comment());
+      out << ",\n    ";
+    } else {
+      out << ", NULL, ";
+    }
+
+    // Extra void* argument; we don't make use of it.
+    out << "NULL};\n";
+
+    out << "    PyDict_SetItemString(dict, \"" << name1 << "\", Dtool_NewStaticProperty(&Dtool_" << ClassName << "._PyType, &def_" << name1 << "));\n";
+    /* Alternative spelling:
+    out << "    PyDict_SetItemString(\"" << name2 << "\", &def_" << name1 << ");\n";
+    */
+  }
+
   out << "    if (PyType_Ready((PyTypeObject *)&Dtool_" << ClassName << ") < 0) {\n"
          "      Dtool_Raise_TypeError(\"PyType_Ready(" << ClassName << ")\");\n"
          "      return;\n"
@@ -6443,11 +6483,15 @@ write_getset(ostream &out, Object *obj, Property *property) {
         "/**\n"
         " * sequence getter for property " << cClassName << "::" << ielem.get_name() << "\n"
         " */\n"
-        "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getitem(PyObject *self, Py_ssize_t index) {\n"
-        "  " << cClassName << " *local_this = NULL;\n"
-        "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
-        "    return NULL;\n"
-        "  }\n";
+        "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getitem(PyObject *self, Py_ssize_t index) {\n";
+      if (property->_getter->_has_this ||
+          (property->_has_function && property->_has_function->_has_this)) {
+        out <<
+          "  " << cClassName << " *local_this = NULL;\n"
+          "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
+          "    return NULL;\n"
+          "  }\n";
+      }
 
       // This is a getitem of a sequence type.  This means we *need* to raise
       // IndexError if we're out of bounds.
@@ -6458,8 +6502,12 @@ write_getset(ostream &out, Object *obj, Property *property) {
       out << "  }\n";
 
       if (property->_has_function != NULL) {
-        out << "  if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n"
-            << "    Py_INCREF(Py_None);\n"
+        if (property->_has_function->_has_this) {
+          out << "  if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n";
+        } else {
+          out << "  if (!" << cClassName << "::" << property->_has_function->_ifunc.get_name() << "(index)) {\n";
+        }
+        out << "    Py_INCREF(Py_None);\n"
             << "    return Py_None;\n"
             << "  }\n";
       }
@@ -6494,16 +6542,22 @@ write_getset(ostream &out, Object *obj, Property *property) {
     // Write out a setitem if this is not a read-only property.
     if (property->_setter != NULL) {
       out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n";
-      out << "  " << cClassName  << " *local_this = NULL;\n";
-      out << "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
-          << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
-      out << "    return -1;\n";
-      out << "  }\n\n";
+      if (property->_has_this) {
+        out << "  " << cClassName  << " *local_this = NULL;\n";
+        out << "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
+            << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
+        out << "    return -1;\n";
+        out << "  }\n\n";
+      }
 
       out << "  if (arg == (PyObject *)NULL) {\n";
       if (property->_deleter != NULL) {
-        out << "    local_this->" << property->_deleter->_ifunc.get_name() << "(index);\n"
-            << "    return 0;\n";
+        if (property->_deleter->_has_this) {
+          out << "    local_this->" << property->_deleter->_ifunc.get_name() << "(index);\n";
+        } else {
+          out << "    " << cClassName << "::" << property->_deleter->_ifunc.get_name() << "(index);\n";
+        }
+        out << "    return 0;\n";
       } else {
         out << "    Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << "[] attribute\");\n"
                "    return -1;\n";
@@ -6511,9 +6565,13 @@ write_getset(ostream &out, Object *obj, Property *property) {
       out << "  }\n";
 
       if (property->_clear_function != NULL) {
-        out << "  if (arg == Py_None) {\n"
-            << "    local_this->" << property->_clear_function->_ifunc.get_name() << "(index);\n"
-            << "    return 0;\n"
+        out << "  if (arg == Py_None) {\n";
+        if (property->_clear_function->_has_this) {
+          out << "    local_this->" << property->_clear_function->_ifunc.get_name() << "(index);\n";
+        } else {
+          out << "    " << cClassName << "::" << property->_clear_function->_ifunc.get_name() << "(index);\n";
+        }
+        out << "    return 0;\n"
             << "  }\n";
       }
 
@@ -6547,9 +6605,14 @@ write_getset(ostream &out, Object *obj, Property *property) {
     }
 
     // Now write the getter, which returns a special wrapper object.
-    out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n"
-           "  Py_INCREF(self);\n"
-           "  Dtool_SequenceWrapper *wrap = PyObject_New(Dtool_SequenceWrapper, &Dtool_SequenceWrapper_Type);\n"
+    out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
+    if (property->_has_this) {
+      out << "  nassertr(self != NULL, NULL);\n"
+             "  Py_INCREF(self);\n";
+    } else {
+      out << "  Py_XINCREF(self);\n";
+    }
+    out << "  Dtool_SequenceWrapper *wrap = PyObject_New(Dtool_SequenceWrapper, &Dtool_SequenceWrapper_Type);\n"
            "  wrap->_base = self;\n"
            "  wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
            "  wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Getitem;\n";
@@ -6566,20 +6629,26 @@ write_getset(ostream &out, Object *obj, Property *property) {
     out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
     FunctionRemap *remap = property->_getter->_remaps.front();
 
-    if (remap->_const_method) {
-      out << "  const " << cClassName  << " *local_this = NULL;\n";
-      out << "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
-    } else {
-      out << "  " << cClassName  << " *local_this = NULL;\n";
-      out << "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
-          << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
+    if (remap->_has_this) {
+      if (remap->_const_method) {
+        out << "  const " << cClassName  << " *local_this = NULL;\n";
+        out << "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
+      } else {
+        out << "  " << cClassName  << " *local_this = NULL;\n";
+        out << "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
+            << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
+      }
+      out << "    return NULL;\n";
+      out << "  }\n\n";
     }
-    out << "    return NULL;\n";
-    out << "  }\n\n";
 
     if (property->_has_function != NULL) {
-      out << "  if (!local_this->" << property->_has_function->_ifunc.get_name() << "()) {\n"
-          << "    Py_INCREF(Py_None);\n"
+      if (remap->_has_this) {
+        out << "  if (!local_this->" << property->_has_function->_ifunc.get_name() << "()) {\n";
+      } else {
+        out << "  if (!" << cClassName << "::" << property->_has_function->_ifunc.get_name() << "()) {\n";
+      }
+      out << "    Py_INCREF(Py_None);\n"
           << "    return Py_None;\n"
           << "  }\n";
     }
@@ -6596,16 +6665,21 @@ write_getset(ostream &out, Object *obj, Property *property) {
     // Write out a setter if this is not a read-only property.
     if (property->_setter != NULL) {
       out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter(PyObject *self, PyObject *arg, void *) {\n";
-      out << "  " << cClassName  << " *local_this = NULL;\n";
-      out << "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
-          << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
-      out << "    return -1;\n";
-      out << "  }\n\n";
+      if (remap->_has_this) {
+        out << "  " << cClassName  << " *local_this = NULL;\n";
+        out << "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
+            << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
+        out << "    return -1;\n";
+        out << "  }\n\n";
+      }
 
       out << "  if (arg == (PyObject *)NULL) {\n";
-      if (property->_deleter != NULL) {
+      if (property->_deleter != NULL && remap->_has_this) {
         out << "    local_this->" << property->_deleter->_ifunc.get_name() << "();\n"
             << "    return 0;\n";
+      } else if (property->_deleter != NULL) {
+        out << "    " << cClassName << "::" << property->_deleter->_ifunc.get_name() << "();\n"
+            << "    return 0;\n";
       } else {
         out << "    Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << " attribute\");\n"
                "    return -1;\n";
@@ -6613,9 +6687,13 @@ write_getset(ostream &out, Object *obj, Property *property) {
       out << "  }\n";
 
       if (property->_clear_function != NULL) {
-        out << "  if (arg == Py_None) {\n"
-            << "    local_this->" << property->_clear_function->_ifunc.get_name() << "();\n"
-            << "    return 0;\n"
+        out << "  if (arg == Py_None) {\n";
+        if (remap->_has_this) {
+          out << "    local_this->" << property->_clear_function->_ifunc.get_name() << "();\n";
+        } else {
+          out << "    " << cClassName << "::" << property->_clear_function->_ifunc.get_name() << "();\n";
+        }
+        out << "    return 0;\n"
             << "  }\n";
       }
 
@@ -6744,6 +6822,7 @@ record_object(TypeIndex type_index) {
       Function *setter = record_function(itype, func_index);
       if (is_function_legal(setter)) {
         property->_setter = setter;
+        property->_has_this |= setter->_has_this;
       }
     }
 
@@ -6752,6 +6831,7 @@ record_object(TypeIndex type_index) {
       Function *getter = record_function(itype, func_index);
       if (is_function_legal(getter)) {
         property->_getter = getter;
+        property->_has_this |= getter->_has_this;
       }
     }
 
@@ -6760,6 +6840,7 @@ record_object(TypeIndex type_index) {
       Function *has_function = record_function(itype, func_index);
       if (is_function_legal(has_function)) {
         property->_has_function = has_function;
+        property->_has_this |= has_function->_has_this;
       }
     }
 
@@ -6768,6 +6849,7 @@ record_object(TypeIndex type_index) {
       Function *clear_function = record_function(itype, func_index);
       if (is_function_legal(clear_function)) {
         property->_clear_function = clear_function;
+        property->_has_this |= clear_function->_has_this;
       }
     }
 
@@ -6776,12 +6858,16 @@ record_object(TypeIndex type_index) {
       Function *del_function = record_function(itype, func_index);
       if (is_function_legal(del_function)) {
         property->_deleter = del_function;
+        property->_has_this |= del_function->_has_this;
       }
     }
 
     if (ielement.is_sequence()) {
       FunctionIndex func_index = ielement.get_length_function();
       property->_length_function = record_function(itype, func_index);
+      if (property->_length_function != nullptr) {
+        property->_has_this |= property->_length_function->_has_this;
+      }
     }
 
     if (property->_getter != NULL) {

+ 138 - 3
dtool/src/interrogatedb/py_panda.cxx

@@ -618,8 +618,11 @@ PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
     dtool_inited = true;
 
     if (PyType_Ready(&Dtool_SequenceWrapper_Type) < 0) {
-      PyErr_SetString(PyExc_TypeError, "PyType_Ready(Dtool_SequenceWrapper)");
-      return NULL;
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_SequenceWrapper)");
+    }
+
+    if (PyType_Ready(&Dtool_StaticProperty_Type) < 0) {
+      return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)");
     }
 
     // Initialize the base class of everything.
@@ -1035,7 +1038,7 @@ bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds)
 static void Dtool_SequenceWrapper_dealloc(PyObject *self) {
   Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
   nassertv(wrap);
-  Py_DECREF(wrap->_base);
+  Py_XDECREF(wrap->_base);
 }
 
 static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) {
@@ -1131,4 +1134,136 @@ PyTypeObject Dtool_SequenceWrapper_Type = {
 #endif
 };
 
+/**
+ * This is a variant of the Python getset mechanism that permits static
+ * properties.
+ */
+PyObject *
+Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
+  PyGetSetDescrObject *descr;
+  descr = (PyGetSetDescrObject *)PyType_GenericAlloc(&Dtool_StaticProperty_Type, 0);
+  if (descr != nullptr) {
+    Py_XINCREF(type);
+    descr->d_getset = (PyGetSetDef *)getset;
+#if PY_MAJOR_VERSION >= 3
+    descr->d_common.d_type = type;
+    descr->d_common.d_name = PyUnicode_InternFromString(getset->name);
+#if PY_VERSION_HEX >= 0x03030000
+    descr->d_common.d_qualname = nullptr;
+#endif
+#else
+    descr->d_type = type;
+    descr->d_name = PyString_InternFromString(getset->name);
+#endif
+  }
+  return (PyObject *)descr;
+}
+
+static void
+Dtool_StaticProperty_dealloc(PyDescrObject *descr) {
+  _PyObject_GC_UNTRACK(descr);
+  Py_XDECREF(descr->d_type);
+  Py_XDECREF(descr->d_name);
+//#if PY_MAJOR_VERSION >= 3
+//  Py_XDECREF(descr->d_qualname);
+//#endif
+  PyObject_GC_Del(descr);
+}
+
+static PyObject *
+Dtool_StaticProperty_repr(PyDescrObject *descr, const char *format) {
+#if PY_MAJOR_VERSION >= 3
+  return PyUnicode_FromFormat("<attribute '%V' of '%s'>", descr->d_name, "?", descr->d_type->tp_name);
+#else
+  return PyString_FromFormat("<attribute '%V' of '%s'>", descr->d_name, "?", descr->d_type->tp_name);
+#endif
+}
+
+static int
+Dtool_StaticProperty_traverse(PyObject *self, visitproc visit, void *arg) {
+  PyDescrObject *descr = (PyDescrObject *)self;
+  Py_VISIT(descr->d_type);
+  return 0;
+}
+
+static PyObject *
+Dtool_StaticProperty_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type) {
+  if (descr->d_getset->get != nullptr) {
+    return descr->d_getset->get(obj, descr->d_getset->closure);
+  } else {
+    return PyErr_Format(PyExc_AttributeError,
+                        "attribute '%V' of type '%.100s' is not readable",
+                        ((PyDescrObject *)descr)->d_name, "?",
+                        ((PyDescrObject *)descr)->d_type->tp_name);
+  }
+}
+
+static int
+Dtool_StaticProperty_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) {
+  if (descr->d_getset->set != nullptr) {
+    return descr->d_getset->set(obj, value, descr->d_getset->closure);
+  } else {
+    PyErr_Format(PyExc_AttributeError,
+                 "attribute '%V' of type '%.100s' is not writable",
+                 ((PyDescrObject *)descr)->d_name, "?",
+                 ((PyDescrObject *)descr)->d_type->tp_name);
+    return -1;
+  }
+}
+
+PyTypeObject Dtool_StaticProperty_Type = {
+  PyVarObject_HEAD_INIT(&PyType_Type, 0)
+  "getset_descriptor",
+  sizeof(PyGetSetDescrObject),
+  0, // tp_itemsize
+  (destructor)Dtool_StaticProperty_dealloc,
+  0, // tp_print
+  0, // tp_getattr
+  0, // tp_setattr
+  0, // tp_reserved
+  (reprfunc)Dtool_StaticProperty_repr,
+  0, // tp_as_number
+  0, // tp_as_sequence
+  0, // tp_as_mapping
+  0, // tp_hash
+  0, // tp_call
+  0, // tp_str
+  PyObject_GenericGetAttr,
+  0, // tp_setattro
+  0, // tp_as_buffer
+  Py_TPFLAGS_DEFAULT,
+  0, // tp_doc
+  Dtool_StaticProperty_traverse,
+  0, // tp_clear
+  0, // tp_richcompare
+  0, // tp_weaklistoffset
+  0, // tp_iter
+  0, // tp_iternext
+  0, // tp_methods
+  0, // tp_members
+  0, // tp_getset
+  0, // tp_base
+  0, // tp_dict
+  (descrgetfunc)Dtool_StaticProperty_get,
+  (descrsetfunc)Dtool_StaticProperty_set,
+  0, // tp_dictoffset
+  0, // tp_init
+  0, // tp_alloc
+  0, // tp_new
+  0, // tp_del
+  0, // tp_is_gc
+  0, // tp_bases
+  0, // tp_mro
+  0, // tp_cache
+  0, // tp_subclasses
+  0, // tp_weaklist
+  0, // tp_del
+#if PY_VERSION_HEX >= 0x02060000
+  0, // tp_version_tag
+#endif
+#if PY_VERSION_HEX >= 0x03040000
+  0, // tp_finalize
+#endif
+};
+
 #endif  // HAVE_PYTHON

+ 3 - 0
dtool/src/interrogatedb/py_panda.h

@@ -471,6 +471,9 @@ struct Dtool_SequenceWrapper {
 };
 
 EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type;
+EXPCL_INTERROGATEDB extern PyTypeObject Dtool_StaticProperty_Type;
+
+EXPCL_INTERROGATEDB PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);
 
 /**
  * These functions check whether the arguments passed to a function conform to

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

@@ -105,6 +105,7 @@ extern "C" {
   EXPCL_PYSTUB int PyObject_Cmp(...);
   EXPCL_PYSTUB int PyObject_Compare(...);
   EXPCL_PYSTUB int PyObject_Free(...);
+  EXPCL_PYSTUB int PyObject_GC_Del(...);
   EXPCL_PYSTUB int PyObject_GenericGetAttr(...);
   EXPCL_PYSTUB int PyObject_GenericSetAttr(...);
   EXPCL_PYSTUB int PyObject_GetAttrString(...);
@@ -223,6 +224,7 @@ extern "C" {
   EXPCL_PYSTUB extern void *PyExc_SystemExit;
   EXPCL_PYSTUB extern void *PyExc_TypeError;
   EXPCL_PYSTUB extern void *PyExc_ValueError;
+  EXPCL_PYSTUB extern void *PyType_Type;
   EXPCL_PYSTUB extern void *_PyThreadState_Current;
   EXPCL_PYSTUB extern void *_Py_FalseStruct;
   EXPCL_PYSTUB extern void *_Py_NoneStruct;
@@ -324,6 +326,7 @@ int PyObject_CallObject(...) { return 0; }
 int PyObject_Cmp(...) { return 0; }
 int PyObject_Compare(...) { return 0; }
 int PyObject_Free(...) { return 0; }
+int PyObject_GC_Del(...) { return 0; }
 int PyObject_GenericGetAttr(...) { return 0; };
 int PyObject_GenericSetAttr(...) { return 0; };
 int PyObject_GetAttrString(...) { return 0; }
@@ -448,6 +451,7 @@ void *PyExc_StopIteration = (void *)NULL;
 void *PyExc_SystemExit = (void *)NULL;
 void *PyExc_TypeError = (void *)NULL;
 void *PyExc_ValueError = (void *)NULL;
+void *PyType_Type = (void *)NULL;
 void *_PyThreadState_Current = (void *)NULL;
 void *_Py_FalseStruct = (void *)NULL;
 void *_Py_NoneStruct = (void *)NULL;

+ 1 - 1
makepanda/config.in

@@ -89,7 +89,7 @@ hardware-animated-vertices #f
 
 # Enable the model-cache, but only for models, not textures.
 
-model-cache-dir $HOME/.panda3d/cache
+model-cache-dir $XDG_CACHE_HOME/panda3d
 model-cache-textures #f
 
 # This option specifies the default profiles for Cg shaders.

+ 23 - 6
makepanda/makepanda.py

@@ -1516,7 +1516,9 @@ def CompileLib(lib, obj, opts):
             if HasTargetArch():
                 cmd += " /MACHINE:" + GetTargetArch().upper()
             cmd += ' /OUT:' + BracketNameWithQuotes(lib)
-            for x in obj: cmd += ' ' + BracketNameWithQuotes(x)
+            for x in obj:
+                if not x.endswith('.lib'):
+                    cmd += ' ' + BracketNameWithQuotes(x)
             oscmd(cmd)
         else:
             # Choose Intel linker; from Jean-Claude
@@ -1569,6 +1571,21 @@ def CompileLink(dll, obj, opts):
             cmd += " /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO "
             cmd += ' /OUT:' + BracketNameWithQuotes(dll)
 
+            if not PkgSkip("PYTHON"):
+                # If we're building without Python, don't pick it up implicitly.
+                if "PYTHON" not in opts:
+                    pythonv = SDK["PYTHONVERSION"].replace('.', '')
+                    if optlevel <= 2:
+                        cmd += ' /NOD:{}d.lib'.format(pythonv)
+                    else:
+                        cmd += ' /NOD:{}.lib'.format(pythonv)
+
+                # Yes, we know we are importing "locally defined symbols".
+                for x in obj:
+                    if x.endswith('libp3pystub.lib'):
+                        cmd += ' /ignore:4049,4217'
+                        break
+
             # Set the subsystem.  Specify that we want to target Windows XP.
             subsystem = GetValueOption(opts, "SUBSYSTEM:") or "CONSOLE"
             cmd += " /SUBSYSTEM:" + subsystem
@@ -2693,7 +2710,7 @@ if '__file__' in locals():
     import os
 
     bindir = os.path.join(os.path.dirname(__file__), '..', 'bin')
-    if os.path.isfile(os.path.join(bindir, 'libpanda.dll')):
+    if os.path.isdir(bindir):
         if not os.environ.get('PATH'):
             os.environ['PATH'] = bindir
         else:
@@ -2811,12 +2828,12 @@ else:
     configprc = ReadFile("makepanda/config.in")
 
 if (GetTarget() == 'windows'):
-    configprc = configprc.replace("$HOME/.panda3d", "$USER_APPDATA/Panda3D-%s" % MAJOR_VERSION)
+    configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "$USER_APPDATA/Panda3D-%s" % MAJOR_VERSION)
 else:
     configprc = configprc.replace("aux-display pandadx9", "")
 
 if (GetTarget() == 'darwin'):
-    configprc = configprc.replace(".panda3d/cache", "Library/Caches/Panda3D-%s" % MAJOR_VERSION)
+    configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "Library/Caches/Panda3D-%s" % MAJOR_VERSION)
 
     # OpenAL is not yet working well on OSX for us, so let's do this for now.
     configprc = configprc.replace("p3openal_audio", "p3fmod_audio")
@@ -4900,7 +4917,7 @@ if (PkgSkip("BULLET")==0 and not RUNTIME):
 #
 
 if (PkgSkip("PHYSX")==0):
-  OPTS=['DIR:panda/src/physx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOARCH:PPC']
+  OPTS=['DIR:panda/src/physx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOARCH:PPC', 'PYTHON']
   TargetAdd('p3physx_composite.obj', opts=OPTS, input='p3physx_composite.cxx')
 
   OPTS=['DIR:panda/src/physx', 'PHYSX', 'NOARCH:PPC', 'PYTHON']
@@ -4920,7 +4937,7 @@ if (PkgSkip("PHYSX")==0):
   TargetAdd('libpandaphysx.dll', input='pandaphysx_pandaphysx.obj')
   TargetAdd('libpandaphysx.dll', input='p3physx_composite.obj')
   TargetAdd('libpandaphysx.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'NOARCH:PPC'])
+  TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'NOARCH:PPC', 'PYTHON'])
 
   OPTS=['DIR:panda/metalibs/pandaphysx', 'PHYSX', 'NOARCH:PPC', 'PYTHON']
   TargetAdd('physx_module.obj', input='libpandaphysx.in')

+ 12 - 0
makepanda/makepandacore.py

@@ -2422,6 +2422,7 @@ def SetupVisualStudioEnviron():
     winsdk_ver = SDK["MSPLATFORM_VERSION"]
     if winsdk_ver.startswith('10.'):
         AddToPathEnv("PATH",    SDK["MSPLATFORM"] + "bin\\" + arch)
+        AddToPathEnv("PATH",    SDK["MSPLATFORM"] + "bin\\" + winsdk_ver + "\\" + arch)
 
         # Windows Kit 10 introduces the "universal CRT".
         inc_dir = SDK["MSPLATFORM"] + "Include\\" + winsdk_ver + "\\"
@@ -3157,6 +3158,17 @@ def TargetAdd(target, dummy=0, opts=[], input=[], dep=[], ipath=None, winrc=None
                 for d in CxxCalcDependencies(fullinput, ipath, []):
                     t.deps[d] = 1
 
+        # If we are linking statically, add the source DLL's dynamic dependencies.
+        if GetLinkAllStatic() and ORIG_EXT[fullinput] == '.lib' and fullinput in TARGET_TABLE:
+            tdep = TARGET_TABLE[fullinput]
+            for y in tdep.inputs:
+                if ORIG_EXT[y] == '.lib':
+                    t.inputs.append(y)
+
+            for opt, _ in LIBNAMES + LIBDIRECTORIES + FRAMEWORKDIRECTORIES:
+                if opt in tdep.opts and opt not in t.opts:
+                    t.opts.append(opt)
+
         if x.endswith(".in"):
             # Mark the _igate.cxx file as a dependency also.
             outbase = os.path.basename(x)[:-3]

+ 2 - 2
panda/src/audio/audioManager.cxx

@@ -170,7 +170,7 @@ get_null_sound() {
  *
  */
 int AudioManager::
-getSpeakerSetup() {
+get_speaker_setup() {
   // intentionally blank
   return 0;
 }
@@ -179,7 +179,7 @@ getSpeakerSetup() {
  *
  */
 void AudioManager::
-setSpeakerSetup(SpeakerModeCategory cat) {
+set_speaker_setup(SpeakerModeCategory cat) {
   // intentionally blank
 }
 

+ 3 - 2
panda/src/audio/audioManager.h

@@ -62,8 +62,8 @@ PUBLISHED:
     SM_stream,
   };
 
-  virtual int getSpeakerSetup();
-  virtual void setSpeakerSetup(SpeakerModeCategory cat);
+  virtual int get_speaker_setup();
+  virtual void set_speaker_setup(SpeakerModeCategory cat);
   virtual bool configure_filters(FilterProperties *config);
 
   // Create an AudioManager for each category of sounds you have.  E.g.
@@ -172,6 +172,7 @@ PUBLISHED:
   virtual PN_stdfloat audio_3d_get_drop_off_factor() const;
 
   static Filename get_dls_pathname();
+  MAKE_PROPERTY(dls_pathname, get_dls_pathname);
 
   virtual void output(ostream &out) const;
   virtual void write(ostream &out) const;

+ 2 - 2
panda/src/audiotraits/fmodAudioManager.cxx

@@ -447,7 +447,7 @@ get_sound(MovieAudio *source, bool positional, int) {
  * This is to query if you are using a MultiChannel Setup.
  */
 int FmodAudioManager::
-getSpeakerSetup() {
+get_speaker_setup() {
   ReMutexHolder holder(_lock);
   FMOD_RESULT result;
   FMOD_SPEAKERMODE speakerMode;
@@ -502,7 +502,7 @@ getSpeakerSetup() {
  * init or re-init the AudioManagers after Panda is running.
  */
 void FmodAudioManager::
-setSpeakerSetup(AudioManager::SpeakerModeCategory cat) {
+set_speaker_setup(AudioManager::SpeakerModeCategory cat) {
   ReMutexHolder holder(_lock);
   FMOD_RESULT result;
   FMOD_SPEAKERMODE speakerModeType = (FMOD_SPEAKERMODE)cat;

+ 2 - 2
panda/src/audiotraits/fmodAudioManager.h

@@ -91,8 +91,8 @@ public:
   virtual PT(AudioSound) get_sound(const string&, bool positional = false, int mode=SM_heuristic);
   virtual PT(AudioSound) get_sound(MovieAudio *,  bool positional = false, int mode=SM_heuristic);
 
-  virtual int getSpeakerSetup();
-  virtual void setSpeakerSetup(SpeakerModeCategory cat);
+  virtual int get_speaker_setup();
+  virtual void set_speaker_setup(SpeakerModeCategory cat);
 
   virtual void set_volume(PN_stdfloat);
   virtual PN_stdfloat get_volume() const;

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

@@ -30,8 +30,8 @@ BulletHeightfieldShape(const BulletHeightfieldShape &copy) :
   _num_rows(copy._num_rows),
   _num_cols(copy._num_cols) {
 
-  _data = new float[_num_rows * _num_cols];
-  memcpy(_data, copy._data, _num_rows * _num_cols * sizeof(float));
+  _data = new btScalar[_num_rows * _num_cols];
+  memcpy(_data, copy._data, _num_rows * _num_cols * sizeof(btScalar));
 }
 
 /**
@@ -44,6 +44,6 @@ operator = (const BulletHeightfieldShape &copy) {
   _num_rows = copy._num_rows;
   _num_cols = copy._num_cols;
 
-  _data = new float[_num_rows * _num_cols];
-  memcpy(_data, copy._data, _num_rows * _num_cols * sizeof(float));
+  _data = new btScalar[_num_rows * _num_cols];
+  memcpy(_data, copy._data, _num_rows * _num_cols * sizeof(btScalar));
 }

+ 5 - 5
panda/src/bullet/bulletHeightfieldShape.cxx

@@ -26,7 +26,7 @@ BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAx
   _num_rows = image.get_x_size();
   _num_cols = image.get_y_size();
 
-  _data = new float[_num_rows * _num_cols];
+  _data = new btScalar[_num_rows * _num_cols];
 
   for (int row=0; row < _num_rows; row++) {
     for (int column=0; column < _num_cols; column++) {
@@ -75,10 +75,10 @@ BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) {
 
   _num_rows = tex->get_x_size() + 1;
   _num_cols = tex->get_y_size() + 1;
-  _data = new float[_num_rows * _num_cols];
+  _data = new btScalar[_num_rows * _num_cols];
 
-  PN_stdfloat step_x = 1.0 / (PN_stdfloat)tex->get_x_size();
-  PN_stdfloat step_y = 1.0 / (PN_stdfloat)tex->get_y_size();
+  btScalar step_x = 1.0 / (btScalar)tex->get_x_size();
+  btScalar step_y = 1.0 / (btScalar)tex->get_y_size();
 
   PT(TexturePeeker) peeker = tex->peek();
   LColor sample;
@@ -100,4 +100,4 @@ BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) {
                                          up,
                                          true, false);
   _shape->setUserPointer(this);
-}
+}

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

@@ -44,7 +44,7 @@ public:
 private:
   int _num_rows;
   int _num_cols;
-  float *_data;
+  btScalar *_data;
   btHeightfieldTerrainShape *_shape;
 
 public:

+ 31 - 7
panda/src/bullet/bulletTriangleMesh.cxx

@@ -53,6 +53,10 @@ void BulletTriangleMesh::
 preallocate(int num_verts, int num_indices) {
   _vertices.reserve(num_verts);
   _indices.reserve(num_indices);
+
+  btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0];
+  mesh.m_vertexBase = (unsigned char*)&_vertices[0];
+  mesh.m_triangleIndexBase = (unsigned char *)&_indices[0];
 }
 
 /**
@@ -160,12 +164,22 @@ add_geom(const Geom *geom, bool remove_duplicate_vertices, const TransformState
         _indices.reserve(_indices.size() + num_vertices);
         mesh.m_numTriangles += num_vertices / 3;
 
-        GeomVertexReader index(prim->get_vertices(), 0);
-        while (!index.is_at_end()) {
-          _indices.push_back(index_offset + index.get_data1i());
+        CPT(GeomVertexArrayData) vertices = prim->get_vertices();
+        if (vertices != nullptr) {
+          GeomVertexReader index(move(vertices), 0);
+          while (!index.is_at_end()) {
+            _indices.push_back(index_offset + index.get_data1i());
+          }
+        } else {
+          int index = index_offset + prim->get_first_vertex();
+          int end_index = index + num_vertices;
+          while (index < end_index) {
+            _indices.push_back(index++);
+          }
         }
       }
     }
+    nassertv(mesh.m_numTriangles * 3 == _indices.size());
 
   } else {
     // Collect points
@@ -193,12 +207,22 @@ add_geom(const Geom *geom, bool remove_duplicate_vertices, const TransformState
         _indices.reserve(_indices.size() + num_vertices);
         mesh.m_numTriangles += num_vertices / 3;
 
-        GeomVertexReader index(prim->get_vertices(), 0);
-        while (!index.is_at_end()) {
-          _indices.push_back(find_or_add_vertex(points[index.get_data1i()]));
+        CPT(GeomVertexArrayData) vertices = prim->get_vertices();
+        if (vertices != nullptr) {
+          GeomVertexReader index(move(vertices), 0);
+          while (!index.is_at_end()) {
+            _indices.push_back(find_or_add_vertex(points[index.get_data1i()]));
+          }
+        } else {
+          int index = prim->get_first_vertex();
+          int end_index = index + num_vertices;
+          while (index < end_index) {
+            _indices.push_back(find_or_add_vertex(points[index]));
+          }
         }
       }
     }
+    nassertv(mesh.m_numTriangles * 3 == _indices.size());
   }
 
   // Reset the pointers, since the vectors may have been reallocated.
@@ -282,7 +306,7 @@ unsigned int BulletTriangleMesh::
 find_or_add_vertex(const LVecBase3 &p) {
   btVector3 vertex = LVecBase3_to_btVector3(p);
 
-  for (unsigned int i = 0; i < _vertices.size(); ++i) {
+  for (int i = 0; i < _vertices.size(); ++i) {
     if ((_vertices[i] - vertex).length2() <= _welding_distance) {
       return i;
     }

+ 15 - 1
panda/src/cocoadisplay/cocoaGraphicsWindow.mm

@@ -277,7 +277,21 @@ process_events() {
       break;
     }
 
-    [NSApp sendEvent: event];
+    // If we're in fullscreen mode, send mouse events directly to the window.
+    NSEventType type = [event type];
+    if (_properties.get_fullscreen() && (
+        type == NSLeftMouseDown || type == NSLeftMouseUp ||
+        type == NSRightMouseDown || type == NSRightMouseUp ||
+        type == NSOtherMouseDown || type == NSOtherMouseUp ||
+        type == NSLeftMouseDragged ||
+        type == NSRightMouseDragged ||
+        type == NSOtherMouseDragged ||
+        type == NSMouseMoved ||
+        type == NSScrollWheel)) {
+      [_window sendEvent: event];
+    } else {
+      [NSApp sendEvent: event];
+    }
   }
 
   if (_window != nil) {

+ 1 - 0
panda/src/collide/collisionNode.h

@@ -75,6 +75,7 @@ PUBLISHED:
   MAKE_PROPERTY(collider_sort, get_collider_sort, set_collider_sort);
 
   INLINE static CollideMask get_default_collide_mask();
+  MAKE_PROPERTY(default_collide_mask, get_default_collide_mask);
 
 protected:
   virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,

+ 1 - 1
panda/src/cull/cullBinBackToFront.cxx

@@ -99,7 +99,7 @@ draw(bool force, Thread *current_thread) {
       data_reader.set_object(object->_munged_data);
       data_reader.check_array_readers();
       geom_reader.set_object(object->_geom);
-      geom_reader.draw(_gsg, object->_munger, &data_reader, force);
+      geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.
       object->draw_callback(_gsg, force, current_thread);

+ 1 - 1
panda/src/cull/cullBinFixed.cxx

@@ -85,7 +85,7 @@ draw(bool force, Thread *current_thread) {
       data_reader.set_object(object->_munged_data);
       data_reader.check_array_readers();
       geom_reader.set_object(object->_geom);
-      geom_reader.draw(_gsg, object->_munger, &data_reader, force);
+      geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.
       object->draw_callback(_gsg, force, current_thread);

+ 1 - 1
panda/src/cull/cullBinFrontToBack.cxx

@@ -99,7 +99,7 @@ draw(bool force, Thread *current_thread) {
       data_reader.set_object(object->_munged_data);
       data_reader.check_array_readers();
       geom_reader.set_object(object->_geom);
-      geom_reader.draw(_gsg, object->_munger, &data_reader, force);
+      geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.
       object->draw_callback(_gsg, force, current_thread);

+ 1 - 1
panda/src/cull/cullBinStateSorted.cxx

@@ -84,7 +84,7 @@ draw(bool force, Thread *current_thread) {
       data_reader.set_object(object->_munged_data);
       data_reader.check_array_readers();
       geom_reader.set_object(object->_geom);
-      geom_reader.draw(_gsg, object->_munger, &data_reader, force);
+      geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.
       object->draw_callback(_gsg, force, current_thread);

+ 1 - 1
panda/src/cull/cullBinUnsorted.cxx

@@ -69,7 +69,7 @@ draw(bool force, Thread *current_thread) {
       data_reader.set_object(object->_munged_data);
       data_reader.check_array_readers();
       geom_reader.set_object(object->_geom);
-      geom_reader.draw(_gsg, object->_munger, &data_reader, force);
+      geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.
       object->draw_callback(_gsg, force, current_thread);

+ 6 - 2
panda/src/display/frameBufferProperties.cxx

@@ -581,9 +581,13 @@ get_quality(const FrameBufferProperties &reqs) const {
     }
   }
 
-  // Bonus for each depth bit.  Extra: 2 per bit.
+  // Bonus for each depth bit.  Extra: 8 per bit.
+  // Please note that the Intel Windows driver only gives extra depth in
+  // combination with a stencil buffer, so we need 8 extra depth bits to
+  // outweigh the penalty of 50 for the unwanted stencil buffer, otherwise we
+  // will end up only getting 16-bit depth.
   if (reqs._property[FBP_depth_bits] != 0) {
-    quality += 2 * _property[FBP_depth_bits];
+    quality += 8 * _property[FBP_depth_bits];
   }
 
   // Bonus for each multisample.  Extra: 2 per sample.

+ 87 - 7
panda/src/display/graphicsStateGuardian.cxx

@@ -44,6 +44,7 @@
 #include "ambientLight.h"
 #include "directionalLight.h"
 #include "pointLight.h"
+#include "sphereLight.h"
 #include "spotlight.h"
 #include "textureReloadRequest.h"
 #include "shaderAttrib.h"
@@ -1122,6 +1123,28 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       return &LMatrix4::ident_mat();
     }
   }
+  case Shader::SMO_texscale_i: {
+    const TexMatrixAttrib *tma;
+    const TextureAttrib *ta;
+    if (_target_rs->get_attrib(ta) && _target_rs->get_attrib(tma) &&
+        index < ta->get_num_on_stages()) {
+      LVecBase3 scale = tma->get_transform(ta->get_on_stage(index))->get_scale();
+      t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,scale[0],scale[1],scale[2],0);
+      return &t;
+    } else {
+      return &LMatrix4::ident_mat();
+    }
+  }
+  case Shader::SMO_texcolor_i: {
+    const TextureAttrib *ta;
+    if (_target_rs->get_attrib(ta) && index < ta->get_num_on_stages()) {
+      TextureStage *ts = ta->get_on_stage(index);
+      t.set_row(3, ts->get_color());
+      return &t;
+    } else {
+      return &LMatrix4::zeros_mat();
+    }
+  }
   case Shader::SMO_tex_is_alpha_i: {
     // This is a hack so we can support both F_alpha and other formats in the
     // default shader, to fix font rendering in GLES2
@@ -1156,9 +1179,14 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     nassertr(!np.is_empty(), &LMatrix4::zeros_mat());
     const PlaneNode *plane_node;
     DCAST_INTO_R(plane_node, np.node(), &LMatrix4::zeros_mat());
-    LPlane p (plane_node->get_plane());
-    p.xform(np.get_net_transform()->get_mat()); // World-space
-    t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,p[0],p[1],p[2],p[3]);
+
+    // Transform plane to world space
+    CPT(TransformState) transform = np.get_net_transform();
+    LPlane plane = plane_node->get_plane();
+    if (!transform->is_identity()) {
+      plane.xform(transform->get_mat());
+    }
+    t.set_row(3, plane);
     return &t;
   }
   case Shader::SMO_apiview_clipplane_i: {
@@ -1195,7 +1223,6 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
   }
   case Shader::SMO_world_to_view: {
     return &(_scene_setup->get_world_transform()->get_mat());
-    break;
   }
   case Shader::SMO_view_to_world: {
     return &(_scene_setup->get_camera_transform()->get_mat());
@@ -1387,6 +1414,62 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     }
     return fetch_specified_member(NodePath(), name, t);
   }
+  case Shader::SMO_light_source_i_packed: {
+    // The light matrix contains COLOR, ATTENUATION, POSITION, VIEWVECTOR
+    const LightAttrib *target_light;
+    _target_rs->get_attrib_def(target_light);
+
+    // We don't count ambient lights, which would be pretty silly to handle
+    // via this mechanism.
+    size_t num_lights = target_light->get_num_non_ambient_lights();
+    if (index >= 0 && (size_t)index < num_lights) {
+      NodePath np = target_light->get_on_light((size_t)index);
+      nassertr(!np.is_empty(), &LMatrix4::ident_mat());
+      PandaNode *node = np.node();
+      Light *light = node->as_light();
+      nassertr(light != nullptr, &LMatrix4::zeros_mat());
+      t.set_row(0, light->get_color());
+      t.set_row(1, light->get_attenuation());
+
+      LMatrix4 mat = np.get_net_transform()->get_mat() *
+        _scene_setup->get_world_transform()->get_mat();
+
+      if (node->is_of_type(DirectionalLight::get_class_type())) {
+        LVecBase3 d = mat.xform_vec(((const DirectionalLight *)node)->get_direction());
+        d.normalize();
+        t.set_row(2, LVecBase4(d, 0));
+        t.set_row(3, LVecBase4(-d, 0));
+
+      } else if (node->is_of_type(LightLensNode::get_class_type())) {
+        const Lens *lens = ((const LightLensNode *)node)->get_lens();
+
+        LPoint3 p = mat.xform_point(lens->get_nodal_point());
+        t.set_row(3, LVecBase4(p));
+
+        // For shadowed point light we need to store near/far.
+        // For spotlight we need to store cutoff angle.
+        if (node->is_of_type(Spotlight::get_class_type())) {
+          PN_stdfloat cutoff = ccos(deg_2_rad(lens->get_hfov() * 0.5f));
+          LVecBase3 d = -(mat.xform_vec(lens->get_view_vector()));
+          t.set_cell(1, 3, ((const Spotlight *)node)->get_exponent());
+          t.set_row(2, LVecBase4(d, cutoff));
+
+        } else if (node->is_of_type(PointLight::get_class_type())) {
+          t.set_cell(1, 3, lens->get_far());
+          t.set_cell(3, 3, lens->get_near());
+
+          if (node->is_of_type(SphereLight::get_class_type())) {
+            t.set_cell(2, 3, ((const SphereLight *)node)->get_radius());
+          }
+        }
+      }
+    } else if (index == 0) {
+      // Apply the default OpenGL lights otherwise.
+      // Special exception for light 0, which defaults to white.
+      t.set_row(0, _light_color_scale);
+    }
+    return &t;
+  }
   default:
     nassertr(false /*should never get here*/, &LMatrix4::ident_mat());
     return &LMatrix4::ident_mat();
@@ -2263,10 +2346,8 @@ finish_decal() {
  */
 bool GraphicsStateGuardian::
 begin_draw_primitives(const GeomPipelineReader *geom_reader,
-                      const GeomMunger *munger,
                       const GeomVertexDataPipelineReader *data_reader,
                       bool force) {
-  _munger = munger;
   _data_reader = data_reader;
 
   // Always draw if we have a shader, since the shader might use a different
@@ -2337,7 +2418,6 @@ draw_points(const GeomPrimitivePipelineReader *, bool) {
  */
 void GraphicsStateGuardian::
 end_draw_primitives() {
-  _munger = NULL;
   _data_reader = NULL;
 }
 

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

@@ -367,7 +367,6 @@ public:
   virtual void finish_decal();
 
   virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader,
-                                     const GeomMunger *munger,
                                      const GeomVertexDataPipelineReader *data_reader,
                                      bool force);
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader,
@@ -496,9 +495,8 @@ protected:
   CPT(ShaderAttrib) _state_shader;
   CPT(ShaderAttrib) _target_shader;
 
-  // These are set by begin_draw_primitives(), and are only valid between
+  // This is set by begin_draw_primitives(), and are only valid between
   // begin_draw_primitives() and end_draw_primitives().
-  CPT(GeomMunger) _munger;
   const GeomVertexDataPipelineReader *_data_reader;
 
   unsigned int _color_write_mask;

+ 6 - 5
panda/src/display/standardMunger.cxx

@@ -343,28 +343,29 @@ munge_state_impl(const RenderState *state) {
 
 #ifdef HAVE_CG
   if (_auto_shader) {
-    CPT(RenderState) shader_state = munged_state->get_auto_shader_state();
     GraphicsStateGuardian *gsg = get_gsg();
     ShaderGenerator *shader_generator = gsg->get_shader_generator();
     if (shader_generator == nullptr) {
       shader_generator = new ShaderGenerator(gsg);
       gsg->set_shader_generator(shader_generator);
     }
-    if (shader_state->_generated_shader == NULL) {
+    if (munged_state->_generated_shader == nullptr) {
       // Cache the generated ShaderAttrib on the shader state.
       GeomVertexAnimationSpec spec;
 
       // Currently we overload this flag to request vertex animation for the
       // shader generator.
       const ShaderAttrib *sattr;
-      shader_state->get_attrib_def(sattr);
+      munged_state->get_attrib_def(sattr);
       if (sattr->get_flag(ShaderAttrib::F_hardware_skinning)) {
         spec.set_hardware(4, true);
       }
 
-      shader_state->_generated_shader = shader_generator->synthesize_shader(shader_state, spec);
+      munged_state->_generated_shader = shader_generator->synthesize_shader(munged_state, spec);
+    }
+    if (munged_state->_generated_shader != nullptr) {
+      munged_state = munged_state->set_attrib(munged_state->_generated_shader);
     }
-    munged_state = munged_state->set_attrib(shader_state->_generated_shader);
   }
 #endif
 

+ 2 - 0
panda/src/display/windowProperties.h

@@ -49,6 +49,8 @@ PUBLISHED:
   static WindowProperties get_default();
   static void set_default(const WindowProperties &default_properties);
   static void clear_default();
+  MAKE_PROPERTY(config_properties, get_config_properties);
+  MAKE_PROPERTY(default, get_default, set_default);
 
   static WindowProperties size(int x_size, int y_size);
 

+ 1 - 3
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -1141,11 +1141,9 @@ end_frame(Thread *current_thread) {
  */
 bool DXGraphicsStateGuardian9::
 begin_draw_primitives(const GeomPipelineReader *geom_reader,
-                      const GeomMunger *munger,
                       const GeomVertexDataPipelineReader *data_reader,
                       bool force) {
-  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger,
-                                                    data_reader, force)) {
+  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, data_reader, force)) {
     return false;
   }
   nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false);

+ 0 - 1
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -106,7 +106,6 @@ public:
   virtual void end_frame(Thread *current_thread);
 
   virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader,
-                                     const GeomMunger *munger,
                                      const GeomVertexDataPipelineReader *data_reader,
                                      bool force);
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader,

+ 5 - 5
panda/src/egg/eggLine.h

@@ -29,9 +29,9 @@ PUBLISHED:
   INLINE EggLine &operator = (const EggLine &copy);
   virtual ~EggLine();
 
-  virtual EggLine *make_copy() const OVERRIDE;
+  virtual EggLine *make_copy() const override;
 
-  virtual void write(ostream &out, int indent_level) const OVERRIDE;
+  virtual void write(ostream &out, int indent_level) const override;
 
   INLINE bool has_thick() const;
   INLINE double get_thick() const;
@@ -39,7 +39,7 @@ PUBLISHED:
   INLINE void clear_thick();
 
 protected:
-  virtual int get_num_lead_vertices() const OVERRIDE;
+  virtual int get_num_lead_vertices() const override;
 
 private:
   double _thick;
@@ -54,10 +54,10 @@ public:
     register_type(_type_handle, "EggLine",
                   EggCompositePrimitive::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {
+  virtual TypeHandle force_init_type() override {
     init_type();
     return get_class_type();
   }

+ 4 - 4
panda/src/egg/eggNurbsCurve.h

@@ -29,7 +29,7 @@ PUBLISHED:
   INLINE EggNurbsCurve(const EggNurbsCurve &copy);
   INLINE EggNurbsCurve &operator = (const EggNurbsCurve &copy);
 
-  virtual EggNurbsCurve *make_copy() const OVERRIDE;
+  virtual EggNurbsCurve *make_copy() const override;
 
   void setup(int order, int num_knots);
 
@@ -50,7 +50,7 @@ PUBLISHED:
   INLINE double get_knot(int k) const;
   MAKE_SEQ(get_knots, get_num_knots, get_knot);
 
-  virtual void write(ostream &out, int indent_level) const OVERRIDE;
+  virtual void write(ostream &out, int indent_level) const override;
 
   MAKE_PROPERTY(order, get_order, set_order);
   MAKE_PROPERTY(degree, get_degree);
@@ -72,10 +72,10 @@ public:
     register_type(_type_handle, "EggNurbsCurve",
                   EggCurve::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {
+  virtual TypeHandle force_init_type() override {
     init_type();
     return get_class_type();
   }

+ 5 - 5
panda/src/egg/eggNurbsSurface.h

@@ -36,7 +36,7 @@ PUBLISHED:
   INLINE EggNurbsSurface(const EggNurbsSurface &copy);
   INLINE EggNurbsSurface &operator = (const EggNurbsSurface &copy);
 
-  virtual EggNurbsSurface *make_copy() const OVERRIDE;
+  virtual EggNurbsSurface *make_copy() const override;
 
   void setup(int u_order, int v_order,
              int num_u_knots, int num_v_knots);
@@ -75,14 +75,14 @@ PUBLISHED:
   MAKE_SEQ(get_v_knots, get_num_v_knots, get_v_knot);
   INLINE EggVertex *get_cv(int ui, int vi) const;
 
-  virtual void write(ostream &out, int indent_level) const OVERRIDE;
+  virtual void write(ostream &out, int indent_level) const override;
 
 public:
   Curves _curves_on_surface;
   Trims _trims;
 
 protected:
-  virtual void r_apply_texmats(EggTextureCollection &textures) OVERRIDE;
+  virtual void r_apply_texmats(EggTextureCollection &textures) override;
 
 private:
   typedef vector_double Knots;
@@ -101,10 +101,10 @@ public:
     register_type(_type_handle, "EggNurbsSurface",
                   EggSurface::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {
+  virtual TypeHandle force_init_type() override {
     init_type();
     return get_class_type();
   }

+ 4 - 4
panda/src/egg/eggPatch.h

@@ -28,9 +28,9 @@ PUBLISHED:
   INLINE EggPatch(const EggPatch &copy);
   INLINE EggPatch &operator = (const EggPatch &copy);
 
-  virtual EggPatch *make_copy() const OVERRIDE;
+  virtual EggPatch *make_copy() const override;
 
-  virtual void write(ostream &out, int indent_level) const OVERRIDE;
+  virtual void write(ostream &out, int indent_level) const override;
 
 public:
   static TypeHandle get_class_type() {
@@ -41,10 +41,10 @@ public:
     register_type(_type_handle, "EggPatch",
                   EggPrimitive::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {
+  virtual TypeHandle force_init_type() override {
     init_type();
     return get_class_type();
   }

+ 5 - 5
panda/src/egg/eggPoint.h

@@ -28,7 +28,7 @@ PUBLISHED:
   INLINE EggPoint(const EggPoint &copy);
   INLINE EggPoint &operator = (const EggPoint &copy);
 
-  virtual EggPoint *make_copy() const OVERRIDE;
+  virtual EggPoint *make_copy() const override;
 
   INLINE bool has_thick() const;
   INLINE double get_thick() const;
@@ -40,9 +40,9 @@ PUBLISHED:
   INLINE void set_perspective(bool perspective);
   INLINE void clear_perspective();
 
-  virtual bool cleanup() OVERRIDE;
+  virtual bool cleanup() override;
 
-  virtual void write(ostream &out, int indent_level) const OVERRIDE;
+  virtual void write(ostream &out, int indent_level) const override;
 
 private:
   enum Flags {
@@ -64,10 +64,10 @@ public:
     register_type(_type_handle, "EggPoint",
                   EggPrimitive::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {
+  virtual TypeHandle force_init_type() override {
     init_type();
     return get_class_type();
   }

+ 5 - 5
panda/src/egg/eggPolygon.h

@@ -27,9 +27,9 @@ PUBLISHED:
   INLINE EggPolygon(const EggPolygon &copy);
   INLINE EggPolygon &operator = (const EggPolygon &copy);
 
-  virtual EggPolygon *make_copy() const OVERRIDE;
+  virtual EggPolygon *make_copy() const override;
 
-  virtual bool cleanup() OVERRIDE;
+  virtual bool cleanup() override;
 
   bool calculate_normal(LNormald &result, CoordinateSystem cs = CS_default) const;
   bool is_planar() const;
@@ -39,7 +39,7 @@ PUBLISHED:
   INLINE bool triangulate_into(EggGroupNode *container, bool convex_also) const;
   PT(EggPolygon) triangulate_in_place(bool convex_also);
 
-  virtual void write(ostream &out, int indent_level) const OVERRIDE;
+  virtual void write(ostream &out, int indent_level) const override;
 
 private:
   bool decomp_concave(EggGroupNode *container, int asum, int x, int y) const;
@@ -55,10 +55,10 @@ public:
     register_type(_type_handle, "EggPolygon",
                   EggPrimitive::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {
+  virtual TypeHandle force_init_type() override {
     init_type();
     return get_class_type();
   }

+ 7 - 7
panda/src/egg/eggTriangleFan.h

@@ -29,14 +29,14 @@ PUBLISHED:
   INLINE EggTriangleFan &operator = (const EggTriangleFan &copy);
   virtual ~EggTriangleFan();
 
-  virtual EggTriangleFan *make_copy() const OVERRIDE;
+  virtual EggTriangleFan *make_copy() const override;
 
-  virtual void write(ostream &out, int indent_level) const OVERRIDE;
-  virtual void apply_first_attribute() OVERRIDE;
+  virtual void write(ostream &out, int indent_level) const override;
+  virtual void apply_first_attribute() override;
 
 protected:
-  virtual int get_num_lead_vertices() const OVERRIDE;
-  virtual bool do_triangulate(EggGroupNode *container) const OVERRIDE;
+  virtual int get_num_lead_vertices() const override;
+  virtual bool do_triangulate(EggGroupNode *container) const override;
 
 public:
   static TypeHandle get_class_type() {
@@ -47,10 +47,10 @@ public:
     register_type(_type_handle, "EggTriangleFan",
                   EggCompositePrimitive::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {
+  virtual TypeHandle force_init_type() override {
     init_type();
     return get_class_type();
   }

+ 6 - 6
panda/src/egg/eggTriangleStrip.h

@@ -29,13 +29,13 @@ PUBLISHED:
   INLINE EggTriangleStrip &operator = (const EggTriangleStrip &copy);
   virtual ~EggTriangleStrip();
 
-  virtual EggTriangleStrip *make_copy() const OVERRIDE;
+  virtual EggTriangleStrip *make_copy() const override;
 
-  virtual void write(ostream &out, int indent_level) const OVERRIDE;
+  virtual void write(ostream &out, int indent_level) const override;
 
 protected:
-  virtual int get_num_lead_vertices() const OVERRIDE;
-  virtual bool do_triangulate(EggGroupNode *container) const OVERRIDE;
+  virtual int get_num_lead_vertices() const override;
+  virtual bool do_triangulate(EggGroupNode *container) const override;
 
 public:
   static TypeHandle get_class_type() {
@@ -46,10 +46,10 @@ public:
     register_type(_type_handle, "EggTriangleStrip",
                   EggCompositePrimitive::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {
+  virtual TypeHandle force_init_type() override {
     init_type();
     return get_class_type();
   }

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

@@ -75,6 +75,9 @@ private:
 
 #include "memoryInfo.I"
 
+#else
+class MemoryInfo;
+
 #endif  // DO_MEMORY_USAGE
 
 #endif

+ 100 - 4
panda/src/express/memoryUsage.I

@@ -16,9 +16,13 @@
  * to true, indicating that this class will be in effect.  If this returns
  * false, the user has indicated not to do any of this.
  */
-INLINE bool MemoryUsage::
+ALWAYS_INLINE bool MemoryUsage::
 get_track_memory_usage() {
+#ifdef DO_MEMORY_USAGE
   return get_global_ptr()->_track_memory_usage;
+#else
+  return false;
+#endif
 }
 
 /**
@@ -26,7 +30,19 @@ get_track_memory_usage() {
  */
 INLINE void MemoryUsage::
 record_pointer(ReferenceCount *ptr) {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_record_pointer(ptr);
+#endif
+}
+
+/**
+ * Indicates that the given pointer has been recently allocated.
+ */
+INLINE void MemoryUsage::
+record_pointer(void *ptr, TypeHandle type) {
+#ifdef DO_MEMORY_USAGE
+  get_global_ptr()->ns_record_pointer(ptr, type);
+#endif
 }
 
 /**
@@ -37,7 +53,9 @@ record_pointer(ReferenceCount *ptr) {
  */
 INLINE void MemoryUsage::
 update_type(ReferenceCount *ptr, TypeHandle type) {
-  get_global_ptr()->ns_update_type(ptr, type);
+#ifdef DO_MEMORY_USAGE
+  get_global_ptr()->ns_update_type((void *)ptr, type);
+#endif
 }
 
 /**
@@ -48,7 +66,21 @@ update_type(ReferenceCount *ptr, TypeHandle type) {
  */
 INLINE void MemoryUsage::
 update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
-  get_global_ptr()->ns_update_type(ptr, typed_ptr);
+#ifdef DO_MEMORY_USAGE
+  get_global_ptr()->ns_update_type((void *)ptr, typed_ptr);
+#endif
+}
+
+/**
+ * Associates the indicated type with the given pointer.  This should be
+ * called by functions (e.g.  the constructor) that know more specifically
+ * what type of thing we've got.
+ */
+INLINE void MemoryUsage::
+update_type(void *ptr, TypeHandle type) {
+#ifdef DO_MEMORY_USAGE
+  get_global_ptr()->ns_update_type(ptr, type);
+#endif
 }
 
 /**
@@ -56,7 +88,9 @@ update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
  */
 INLINE void MemoryUsage::
 remove_pointer(ReferenceCount *ptr) {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_remove_pointer(ptr);
+#endif
 }
 
 /**
@@ -65,7 +99,11 @@ remove_pointer(ReferenceCount *ptr) {
  */
 INLINE bool MemoryUsage::
 is_tracking() {
+#ifdef DO_MEMORY_USAGE
   return get_global_ptr()->_track_memory_usage;
+#else
+  return false;
+#endif
 }
 
 /**
@@ -75,7 +113,11 @@ is_tracking() {
  */
 INLINE bool MemoryUsage::
 is_counting() {
+#ifdef DO_MEMORY_USAGE
   return get_global_ptr()->_count_memory_usage;
+#else
+  return false;
+#endif
 }
 
 /**
@@ -84,7 +126,11 @@ is_counting() {
  */
 INLINE size_t MemoryUsage::
 get_current_cpp_size() {
+#ifdef DO_MEMORY_USAGE
   return get_global_ptr()->_current_cpp_size;
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -93,7 +139,11 @@ get_current_cpp_size() {
  */
 INLINE size_t MemoryUsage::
 get_total_cpp_size() {
+#ifdef DO_MEMORY_USAGE
   return get_global_ptr()->_total_cpp_size;
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -102,7 +152,11 @@ get_total_cpp_size() {
  */
 INLINE size_t MemoryUsage::
 get_panda_heap_single_size() {
+#ifdef DO_MEMORY_USAGE
   return (size_t)AtomicAdjust::get(get_global_ptr()->_total_heap_single_size);
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -111,7 +165,11 @@ get_panda_heap_single_size() {
  */
 INLINE size_t MemoryUsage::
 get_panda_heap_array_size() {
+#ifdef DO_MEMORY_USAGE
   return (size_t)AtomicAdjust::get(get_global_ptr()->_total_heap_array_size);
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -121,7 +179,7 @@ get_panda_heap_array_size() {
  */
 INLINE size_t MemoryUsage::
 get_panda_heap_overhead() {
-#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
+#if defined(DO_MEMORY_USAGE) && (defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2))
   MemoryUsage *mu = get_global_ptr();
   return (size_t)(AtomicAdjust::get(mu->_requested_heap_size) - AtomicAdjust::get(mu->_total_heap_single_size) - AtomicAdjust::get(mu->_total_heap_array_size));
 #else
@@ -135,7 +193,11 @@ get_panda_heap_overhead() {
  */
 INLINE size_t MemoryUsage::
 get_panda_mmap_size() {
+#ifdef DO_MEMORY_USAGE
   return (size_t)AtomicAdjust::get(get_global_ptr()->_total_mmap_size);
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -152,6 +214,7 @@ get_panda_mmap_size() {
  */
 INLINE size_t MemoryUsage::
 get_external_size() {
+#ifdef DO_MEMORY_USAGE
   MemoryUsage *mu = get_global_ptr();
   if (mu->_count_memory_usage) {
     // We can only possibly know this with memory counting, which tracks every
@@ -169,6 +232,9 @@ get_external_size() {
   } else {
     return 0;
   }
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -177,6 +243,7 @@ get_external_size() {
  */
 INLINE size_t MemoryUsage::
 get_total_size() {
+#ifdef DO_MEMORY_USAGE
   MemoryUsage *mu = get_global_ptr();
   if (mu->_count_memory_usage) {
     return mu->_total_size + (size_t)mu->_requested_heap_size;
@@ -187,6 +254,9 @@ get_total_size() {
     return (size_t)(AtomicAdjust::get(mu->_total_heap_single_size) + AtomicAdjust::get(mu->_total_heap_array_size));
 #endif
   }
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -194,7 +264,11 @@ get_total_size() {
  */
 INLINE int MemoryUsage::
 get_num_pointers() {
+#ifdef DO_MEMORY_USAGE
   return get_global_ptr()->ns_get_num_pointers();
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -203,7 +277,9 @@ get_num_pointers() {
  */
 INLINE void MemoryUsage::
 get_pointers(MemoryUsagePointers &result) {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_get_pointers(result);
+#endif
 }
 
 /**
@@ -212,7 +288,9 @@ get_pointers(MemoryUsagePointers &result) {
  */
 INLINE void MemoryUsage::
 get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_get_pointers_of_type(result, type);
+#endif
 }
 
 /**
@@ -221,7 +299,9 @@ get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
  */
 INLINE void MemoryUsage::
 get_pointers_of_age(MemoryUsagePointers &result, double from, double to) {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_get_pointers_of_age(result, from, to);
+#endif
 }
 
 /**
@@ -242,7 +322,9 @@ get_pointers_of_age(MemoryUsagePointers &result, double from, double to) {
  */
 INLINE void MemoryUsage::
 get_pointers_with_zero_count(MemoryUsagePointers &result) {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_get_pointers_with_zero_count(result);
+#endif
 }
 
 /**
@@ -253,7 +335,9 @@ get_pointers_with_zero_count(MemoryUsagePointers &result) {
  */
 INLINE void MemoryUsage::
 freeze() {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_freeze();
+#endif
 }
 
 /**
@@ -261,7 +345,9 @@ freeze() {
  */
 INLINE void MemoryUsage::
 show_current_types() {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_show_current_types();
+#endif
 }
 
 /**
@@ -270,7 +356,9 @@ show_current_types() {
  */
 INLINE void MemoryUsage::
 show_trend_types() {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_show_trend_types();
+#endif
 }
 
 /**
@@ -278,7 +366,9 @@ show_trend_types() {
  */
 INLINE void MemoryUsage::
 show_current_ages() {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_show_current_ages();
+#endif
 }
 
 /**
@@ -287,7 +377,9 @@ show_current_ages() {
  */
 INLINE void MemoryUsage::
 show_trend_ages() {
+#ifdef DO_MEMORY_USAGE
   get_global_ptr()->ns_show_trend_ages();
+#endif
 }
 
 /**
@@ -295,6 +387,7 @@ show_trend_ages() {
  */
 INLINE MemoryUsage *MemoryUsage::
 get_global_ptr() {
+#ifdef DO_MEMORY_USAGE
 #ifdef __GNUC__
   // Tell the compiler that this is an unlikely branch.
   if (__builtin_expect(_global_ptr == nullptr, 0)) {
@@ -305,4 +398,7 @@ get_global_ptr() {
   }
 
   return _global_ptr;
+#else
+  return nullptr;
+#endif
 }

+ 157 - 23
panda/src/express/memoryUsage.cxx

@@ -12,9 +12,6 @@
  */
 
 #include "memoryUsage.h"
-
-#ifdef DO_MEMORY_USAGE
-
 #include "memoryUsagePointers.h"
 #include "trueClock.h"
 #include "typedReferenceCount.h"
@@ -45,16 +42,17 @@ double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets
   60.0,
 };
 
-
 /**
  * Adds a single entry to the histogram.
  */
 void MemoryUsage::TypeHistogram::
 add_info(TypeHandle type, MemoryInfo *info) {
+#ifdef DO_MEMORY_USAGE
   _counts[type].add_info(info);
+#endif
 }
 
-
+#ifdef DO_MEMORY_USAGE
 // This class is a temporary class used only in TypeHistogram::show(), below,
 // to sort the types in descending order by counts.
 class TypeHistogramCountSorter {
@@ -71,12 +69,14 @@ public:
   MemoryUsagePointerCounts _count;
   TypeHandle _type;
 };
+#endif
 
 /**
  * Shows the contents of the histogram to nout.
  */
 void MemoryUsage::TypeHistogram::
 show() const {
+#ifdef DO_MEMORY_USAGE
   // First, copy the relevant information to a vector so we can sort by
   // counts.  Don't use a pvector.
   typedef vector<TypeHistogramCountSorter> CountSorter;
@@ -99,6 +99,7 @@ show() const {
     }
     nout << " : " << (*vi)._count << "\n";
   }
+#endif
 }
 
 /**
@@ -122,9 +123,11 @@ AgeHistogram() {
  */
 void MemoryUsage::AgeHistogram::
 add_info(double age, MemoryInfo *info) {
+#ifdef DO_MEMORY_USAGE
   int bucket = choose_bucket(age);
   nassertv(bucket >= 0 && bucket < num_buckets);
   _counts[bucket].add_info(info);
+#endif
 }
 
 /**
@@ -132,6 +135,7 @@ add_info(double age, MemoryInfo *info) {
  */
 void MemoryUsage::AgeHistogram::
 show() const {
+#ifdef DO_MEMORY_USAGE
   for (int i = 0; i < num_buckets - 1; i++) {
     nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : ";
     _counts[i].output(nout);
@@ -140,6 +144,7 @@ show() const {
   nout << _cutoff[num_buckets - 1] << " seconds old and up : ";
   _counts[num_buckets - 1].output(nout);
   nout << "\n";
+#endif
 }
 
 /**
@@ -147,9 +152,11 @@ show() const {
  */
 void MemoryUsage::AgeHistogram::
 clear() {
+#ifdef DO_MEMORY_USAGE
   for (int i = 0; i < num_buckets; i++) {
     _counts[i].clear();
   }
+#endif
 }
 
 /**
@@ -157,6 +164,7 @@ clear() {
  */
 int MemoryUsage::AgeHistogram::
 choose_bucket(double age) const {
+#ifdef DO_MEMORY_USAGE
   for (int i = num_buckets - 1; i >= 0; i--) {
     if (age >= _cutoff[i]) {
       return i;
@@ -164,6 +172,7 @@ choose_bucket(double age) const {
   }
   express_cat.error()
     << "No suitable bucket for age " << age << "\n";
+#endif
   return 0;
 }
 
@@ -173,6 +182,7 @@ choose_bucket(double age) const {
  */
 void *MemoryUsage::
 heap_alloc_single(size_t size) {
+#ifdef DO_MEMORY_USAGE
   void *ptr;
 
   if (_recursion_protect) {
@@ -202,6 +212,9 @@ heap_alloc_single(size_t size) {
   }
 
   return ptr;
+#else
+  return MemoryHook::heap_alloc_single(size);
+#endif
 }
 
 /**
@@ -209,6 +222,7 @@ heap_alloc_single(size_t size) {
  */
 void MemoryUsage::
 heap_free_single(void *ptr) {
+#ifdef DO_MEMORY_USAGE
   if (_recursion_protect) {
     if (express_cat.is_spam()) {
       express_cat.spam()
@@ -231,6 +245,9 @@ heap_free_single(void *ptr) {
       MemoryHook::heap_free_single(ptr);
     }
   }
+#else
+  MemoryHook::heap_free_single(ptr);
+#endif
 }
 
 /**
@@ -239,6 +256,7 @@ heap_free_single(void *ptr) {
  */
 void *MemoryUsage::
 heap_alloc_array(size_t size) {
+#ifdef DO_MEMORY_USAGE
   void *ptr;
 
   if (_recursion_protect) {
@@ -268,6 +286,9 @@ heap_alloc_array(size_t size) {
   }
 
   return ptr;
+#else
+  return MemoryHook::heap_alloc_array(size);
+#endif
 }
 
 /**
@@ -275,6 +296,7 @@ heap_alloc_array(size_t size) {
  */
 void *MemoryUsage::
 heap_realloc_array(void *ptr, size_t size) {
+#ifdef DO_MEMORY_USAGE
   if (_recursion_protect) {
     ptr = MemoryHook::heap_realloc_array(ptr, size);
     if (express_cat.is_spam()) {
@@ -303,6 +325,9 @@ heap_realloc_array(void *ptr, size_t size) {
   }
 
   return ptr;
+#else
+  return MemoryHook::heap_realloc_array(ptr, size);
+#endif
 }
 
 /**
@@ -310,6 +335,7 @@ heap_realloc_array(void *ptr, size_t size) {
  */
 void MemoryUsage::
 heap_free_array(void *ptr) {
+#ifdef DO_MEMORY_USAGE
   if (_recursion_protect) {
     if (express_cat.is_spam()) {
       express_cat.spam()
@@ -332,6 +358,9 @@ heap_free_array(void *ptr) {
       MemoryHook::heap_free_array(ptr);
     }
   }
+#else
+  MemoryHook::heap_free_array(ptr);
+#endif
 }
 
 /**
@@ -343,6 +372,7 @@ heap_free_array(void *ptr) {
  */
 void MemoryUsage::
 mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr) {
+#ifdef DO_MEMORY_USAGE
   if (_recursion_protect || !_track_memory_usage) {
     return;
   }
@@ -391,6 +421,7 @@ mark_pointer(void *ptr, size_t size, ReferenceCount *ref_ptr) {
     // We're removing this pointer from use.
     ns_remove_void_pointer(ptr);
   }
+#endif
 }
 
 #if (defined(WIN32_VC) || defined (WIN64_VC))&& defined(_DEBUG)
@@ -430,7 +461,23 @@ win32_malloc_hook(int alloc_type, void *ptr,
  *
  */
 MemoryUsage::
-MemoryUsage(const MemoryHook &copy) : MemoryHook(copy) {
+MemoryUsage(const MemoryHook &copy) :
+  MemoryHook(copy),
+  _info_set_dirty(false),
+  _freeze_index(0),
+  _count(0),
+  _current_cpp_size(0),
+  _total_cpp_size(0),
+  _total_size(0),
+
+  _track_memory_usage(false),
+  _startup_track_memory_usage(false),
+  _count_memory_usage(false),
+  _report_memory_usage(false),
+  _report_memory_interval(0.0),
+  _last_report_time(0.0) {
+
+#ifdef DO_MEMORY_USAGE
   // We must get these variables here instead of in config_express.cxx,
   // because we need to know it at static init time, and who knows when the
   // code in config_express will be executed.
@@ -456,9 +503,6 @@ MemoryUsage(const MemoryHook &copy) : MemoryHook(copy) {
     ("report-memory-interval", 5.0,
      PRC_DESC("This is the interval, in seconds, for reports of currently allocated "
               "memory, when report-memory-usage is true."));
-  _last_report_time = 0.0;
-
-  _count_memory_usage = false;
 
   int64_t max_heap_size = ConfigVariableInt64
     ("max-heap-size", 0,
@@ -480,13 +524,7 @@ MemoryUsage(const MemoryHook &copy) : MemoryHook(copy) {
   _CrtSetAllocHook(&win32_malloc_hook);
   _count_memory_usage = true;
 #endif
-
-  _info_set_dirty = false;
-  _freeze_index = 0;
-  _count = 0;
-  _current_cpp_size = 0;
-  _total_cpp_size = 0;
-  _total_size = 0;
+#endif  // DO_MEMORY_USAGE
 }
 
 /**
@@ -494,9 +532,16 @@ MemoryUsage(const MemoryHook &copy) : MemoryHook(copy) {
  */
 void MemoryUsage::
 init_memory_usage() {
+#ifdef DO_MEMORY_USAGE
   init_memory_hook();
   _global_ptr = new MemoryUsage(*memory_hook);
   memory_hook = _global_ptr;
+#else
+  // If this gets called, we still need to initialize the global_ptr with a
+  // stub even if we don't compile with memory usage tracking enabled, for ABI
+  // stability.  However, we won't replace the memory hook.
+  _global_ptr = new MemoryUsage(*memory_hook);
+#endif
 }
 
 /**
@@ -507,6 +552,7 @@ init_memory_usage() {
  */
 void MemoryUsage::
 overflow_heap_size() {
+#ifdef DO_MEMORY_USAGE
   MemoryHook::overflow_heap_size();
 
   express_cat.error()
@@ -524,6 +570,7 @@ overflow_heap_size() {
   // Turn on spamful debugging.
   _track_memory_usage = true;
   _report_memory_usage = true;
+#endif
 }
 
 /**
@@ -531,12 +578,13 @@ overflow_heap_size() {
  */
 void MemoryUsage::
 ns_record_pointer(ReferenceCount *ptr) {
+#ifdef DO_MEMORY_USAGE
   if (_track_memory_usage) {
     // We have to protect modifications to the table from recursive calls by
     // toggling _recursion_protect while we adjust it.
     _recursion_protect = true;
     pair<Table::iterator, bool> insert_result =
-      _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL));
+      _table.insert(Table::value_type((void *)ptr, nullptr));
 
     // This shouldn't fail.
     assert(insert_result.first != _table.end());
@@ -575,6 +623,61 @@ ns_record_pointer(ReferenceCount *ptr) {
       }
     }
   }
+#endif
+}
+
+
+/**
+ * Indicates that the given pointer has been recently allocated.
+ */
+void MemoryUsage::
+ns_record_pointer(void *ptr, TypeHandle type) {
+#ifdef DO_MEMORY_USAGE
+  if (_track_memory_usage) {
+    // We have to protect modifications to the table from recursive calls by
+    // toggling _recursion_protect while we adjust it.
+    _recursion_protect = true;
+    pair<Table::iterator, bool> insert_result =
+      _table.insert(Table::value_type(ptr, nullptr));
+
+    // This shouldn't fail.
+    assert(insert_result.first != _table.end());
+
+    if (insert_result.second) {
+      (*insert_result.first).second = new MemoryInfo;
+      _info_set_dirty = true;
+      ++_count;
+    }
+
+    MemoryInfo *info = (*insert_result.first).second;
+
+    // We should already have a pointer, thanks to a previous call to
+    // mark_pointer().
+    nassertv(info->_void_ptr == ptr && info->_ref_ptr == nullptr);
+
+    info->_void_ptr = ptr;
+    info->_static_type = type;
+    info->_dynamic_type = type;
+    info->_time = TrueClock::get_global_ptr()->get_long_time();
+    info->_freeze_index = _freeze_index;
+    info->_flags |= MemoryInfo::F_reconsider_dynamic_type;
+
+    // We close the recursion_protect flag all the way down here, so that we
+    // also protect ourselves against a possible recursive call in
+    // TrueClock::get_global_ptr().
+    _recursion_protect = false;
+
+    if (_report_memory_usage) {
+      double now = TrueClock::get_global_ptr()->get_long_time();
+      if (now - _last_report_time > _report_memory_interval) {
+        _last_report_time = now;
+        express_cat.info()
+          << "*** Current memory usage: " << get_total_size() << "\n";
+        show_current_types();
+      }
+    }
+  }
+#endif
 }
 
 /**
@@ -584,7 +687,8 @@ ns_record_pointer(ReferenceCount *ptr) {
  * only that it's a "ReferenceCount".
  */
 void MemoryUsage::
-ns_update_type(ReferenceCount *ptr, TypeHandle type) {
+ns_update_type(void *ptr, TypeHandle type) {
+#ifdef DO_MEMORY_USAGE
   if (_track_memory_usage) {
     Table::iterator ti;
     ti = _table.find(ptr);
@@ -592,7 +696,7 @@ ns_update_type(ReferenceCount *ptr, TypeHandle type) {
       if (_startup_track_memory_usage) {
         express_cat.error()
           << "Attempt to update type to " << type << " for unrecorded pointer "
-          << (void *)ptr << "!\n";
+          << ptr << "!\n";
         nassertv(false);
       }
       return;
@@ -605,6 +709,7 @@ ns_update_type(ReferenceCount *ptr, TypeHandle type) {
 
     consolidate_void_ptr(info);
   }
+#endif
 }
 
 /**
@@ -614,7 +719,8 @@ ns_update_type(ReferenceCount *ptr, TypeHandle type) {
  * the pointer as a TypedObject it doesn't need any more help.
  */
 void MemoryUsage::
-ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
+ns_update_type(void *ptr, TypedObject *typed_ptr) {
+#ifdef DO_MEMORY_USAGE
   if (_track_memory_usage) {
     Table::iterator ti;
     ti = _table.find(ptr);
@@ -623,7 +729,7 @@ ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
         express_cat.error()
           << "Attempt to update type to " << typed_ptr->get_type()
           << " for unrecorded pointer "
-          << (void *)ptr << "!\n";
+          << ptr << "!\n";
       }
       return;
     }
@@ -634,6 +740,7 @@ ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
 
     consolidate_void_ptr(info);
   }
+#endif
 }
 
 /**
@@ -641,6 +748,7 @@ ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) {
  */
 void MemoryUsage::
 ns_remove_pointer(ReferenceCount *ptr) {
+#ifdef DO_MEMORY_USAGE
   if (_track_memory_usage) {
     Table::iterator ti;
     ti = _table.find(ptr);
@@ -705,6 +813,7 @@ ns_remove_pointer(ReferenceCount *ptr) {
       }
     }
   }
+#endif
 }
 
 /**
@@ -713,6 +822,7 @@ ns_remove_pointer(ReferenceCount *ptr) {
  */
 void MemoryUsage::
 ns_record_void_pointer(void *ptr, size_t size) {
+#ifdef DO_MEMORY_USAGE
   if (_track_memory_usage) {
     if (express_cat.is_spam()) {
       express_cat.spam()
@@ -724,7 +834,7 @@ ns_record_void_pointer(void *ptr, size_t size) {
 
     _recursion_protect = true;
     pair<Table::iterator, bool> insert_result =
-      _table.insert(Table::value_type((void *)ptr, (MemoryInfo *)NULL));
+      _table.insert(Table::value_type((void *)ptr, nullptr));
 
     assert(insert_result.first != _table.end());
 
@@ -761,6 +871,7 @@ ns_record_void_pointer(void *ptr, size_t size) {
     // TrueClock::get_global_ptr().
     _recursion_protect = false;
   }
+#endif
 }
 
 /**
@@ -768,6 +879,7 @@ ns_record_void_pointer(void *ptr, size_t size) {
  */
 void MemoryUsage::
 ns_remove_void_pointer(void *ptr) {
+#ifdef DO_MEMORY_USAGE
   if (_track_memory_usage) {
     if (express_cat.is_spam()) {
       express_cat.spam()
@@ -823,6 +935,7 @@ ns_remove_void_pointer(void *ptr) {
     _info_set_dirty = true;
     delete info;
   }
+#endif
 }
 
 /**
@@ -830,8 +943,12 @@ ns_remove_void_pointer(void *ptr) {
  */
 int MemoryUsage::
 ns_get_num_pointers() {
+#ifdef DO_MEMORY_USAGE
   nassertr(_track_memory_usage, 0);
   return _count;
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -840,6 +957,7 @@ ns_get_num_pointers() {
  */
 void MemoryUsage::
 ns_get_pointers(MemoryUsagePointers &result) {
+#ifdef DO_MEMORY_USAGE
   nassertv(_track_memory_usage);
   result.clear();
 
@@ -857,6 +975,7 @@ ns_get_pointers(MemoryUsagePointers &result) {
                        now - info->_time);
     }
   }
+#endif
 }
 
 /**
@@ -865,6 +984,7 @@ ns_get_pointers(MemoryUsagePointers &result) {
  */
 void MemoryUsage::
 ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
+#ifdef DO_MEMORY_USAGE
   nassertv(_track_memory_usage);
   result.clear();
 
@@ -886,6 +1006,7 @@ ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
       }
     }
   }
+#endif
 }
 
 /**
@@ -895,6 +1016,7 @@ ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) {
 void MemoryUsage::
 ns_get_pointers_of_age(MemoryUsagePointers &result,
                        double from, double to) {
+#ifdef DO_MEMORY_USAGE
   nassertv(_track_memory_usage);
   result.clear();
 
@@ -915,6 +1037,7 @@ ns_get_pointers_of_age(MemoryUsagePointers &result,
       }
     }
   }
+#endif
 }
 
 /**
@@ -935,6 +1058,7 @@ ns_get_pointers_of_age(MemoryUsagePointers &result,
  */
 void MemoryUsage::
 ns_get_pointers_with_zero_count(MemoryUsagePointers &result) {
+#ifdef DO_MEMORY_USAGE
   nassertv(_track_memory_usage);
   result.clear();
 
@@ -955,6 +1079,7 @@ ns_get_pointers_with_zero_count(MemoryUsagePointers &result) {
       }
     }
   }
+#endif
 }
 
 /**
@@ -965,11 +1090,13 @@ ns_get_pointers_with_zero_count(MemoryUsagePointers &result) {
  */
 void MemoryUsage::
 ns_freeze() {
+#ifdef DO_MEMORY_USAGE
   _count = 0;
   _current_cpp_size = 0;
   _trend_types.clear();
   _trend_ages.clear();
   _freeze_index++;
+#endif
 }
 
 /**
@@ -977,6 +1104,7 @@ ns_freeze() {
  */
 void MemoryUsage::
 ns_show_current_types() {
+#ifdef DO_MEMORY_USAGE
   nassertv(_track_memory_usage);
   TypeHistogram hist;
 
@@ -994,6 +1122,7 @@ ns_show_current_types() {
   }
   hist.show();
   _recursion_protect = false;
+#endif
 }
 
 /**
@@ -1002,7 +1131,9 @@ ns_show_current_types() {
  */
 void MemoryUsage::
 ns_show_trend_types() {
+#ifdef DO_MEMORY_USAGE
   _trend_types.show();
+#endif
 }
 
 /**
@@ -1010,6 +1141,7 @@ ns_show_trend_types() {
  */
 void MemoryUsage::
 ns_show_current_ages() {
+#ifdef DO_MEMORY_USAGE
   nassertv(_track_memory_usage);
 
   AgeHistogram hist;
@@ -1026,6 +1158,7 @@ ns_show_current_ages() {
 
   hist.show();
   _recursion_protect = false;
+#endif
 }
 
 /**
@@ -1037,6 +1170,8 @@ ns_show_trend_ages() {
   _trend_ages.show();
 }
 
+#ifdef DO_MEMORY_USAGE
+
 /**
  * If the size information has not yet been determined for this pointer,
  * checks to see if it has possibly been recorded under the TypedObject
@@ -1126,5 +1261,4 @@ refresh_info_set() {
   _info_set_dirty = false;
 }
 
-
 #endif  // DO_MEMORY_USAGE

+ 25 - 10
panda/src/express/memoryUsage.h

@@ -15,9 +15,6 @@
 #define MEMORYUSAGE_H
 
 #include "pandabase.h"
-
-#ifdef DO_MEMORY_USAGE
-
 #include "typedObject.h"
 #include "memoryInfo.h"
 #include "memoryUsagePointerCounts.h"
@@ -33,18 +30,22 @@ class MemoryUsagePointers;
  * every such object currently allocated.
  *
  * When compiled with NDEBUG set, this entire class does nothing and compiles
- * to nothing.
+ * to a stub.
  */
 class EXPCL_PANDAEXPRESS MemoryUsage : public MemoryHook {
 public:
-  INLINE static bool get_track_memory_usage();
+  ALWAYS_INLINE static bool get_track_memory_usage();
 
   INLINE static void record_pointer(ReferenceCount *ptr);
+  INLINE static void record_pointer(void *ptr, TypeHandle type);
   INLINE static void update_type(ReferenceCount *ptr, TypeHandle type);
   INLINE static void update_type(ReferenceCount *ptr, TypedObject *typed_ptr);
+  INLINE static void update_type(void *ptr, TypeHandle type);
   INLINE static void remove_pointer(ReferenceCount *ptr);
 
-public:
+protected:
+  // These are not marked public, but they can be accessed via the MemoryHook
+  // base class.
   virtual void *heap_alloc_single(size_t size);
   virtual void heap_free_single(void *ptr);
 
@@ -88,6 +89,19 @@ PUBLISHED:
   INLINE static void show_current_ages();
   INLINE static void show_trend_ages();
 
+PUBLISHED:
+  MAKE_PROPERTY(tracking, is_tracking);
+  MAKE_PROPERTY(counting, is_counting);
+  MAKE_PROPERTY(current_cpp_size, get_current_cpp_size);
+  MAKE_PROPERTY(total_cpp_size, get_total_cpp_size);
+
+  MAKE_PROPERTY(panda_heap_single_size, get_panda_heap_single_size);
+  MAKE_PROPERTY(panda_heap_array_size, get_panda_heap_array_size);
+  MAKE_PROPERTY(panda_heap_overhead, get_panda_heap_overhead);
+  MAKE_PROPERTY(panda_mmap_size, get_panda_mmap_size);
+  MAKE_PROPERTY(external_size, get_external_size);
+  MAKE_PROPERTY(total_size, get_total_size);
+
 protected:
   virtual void overflow_heap_size();
 
@@ -98,8 +112,9 @@ private:
   static void init_memory_usage();
 
   void ns_record_pointer(ReferenceCount *ptr);
-  void ns_update_type(ReferenceCount *ptr, TypeHandle type);
-  void ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr);
+  void ns_record_pointer(void *ptr, TypeHandle type);
+  void ns_update_type(void *ptr, TypeHandle type);
+  void ns_update_type(void *ptr, TypedObject *typed_ptr);
   void ns_remove_pointer(ReferenceCount *ptr);
 
   void ns_record_void_pointer(void *ptr, size_t size);
@@ -120,8 +135,10 @@ private:
   void ns_show_current_ages();
   void ns_show_trend_ages();
 
+#ifdef DO_MEMORY_USAGE
   void consolidate_void_ptr(MemoryInfo *info);
   void refresh_info_set();
+#endif
 
   static MemoryUsage *_global_ptr;
 
@@ -196,6 +213,4 @@ private:
 
 #include "memoryUsage.I"
 
-#endif  // DO_MEMORY_USAGE
-
 #endif

+ 6 - 5
panda/src/express/memoryUsagePointerCounts.cxx

@@ -12,9 +12,6 @@
  */
 
 #include "memoryUsagePointerCounts.h"
-
-#ifdef DO_MEMORY_USAGE
-
 #include "memoryInfo.h"
 
 /**
@@ -22,6 +19,7 @@
  */
 void MemoryUsagePointerCounts::
 add_info(MemoryInfo *info) {
+#ifdef DO_MEMORY_USAGE
   _count++;
 
   if (info->is_size_known()) {
@@ -29,6 +27,7 @@ add_info(MemoryInfo *info) {
   } else {
     _unknown_size_count++;
   }
+#endif
 }
 
 /**
@@ -36,6 +35,7 @@ add_info(MemoryInfo *info) {
  */
 void MemoryUsagePointerCounts::
 output(ostream &out) const {
+#ifdef DO_MEMORY_USAGE
   out << _count << " pointers";
   if (_unknown_size_count < _count) {
     out << ", ";
@@ -48,6 +48,7 @@ output(ostream &out) const {
       out << " (" << _unknown_size_count << " of unknown size)";
     }
   }
+#endif
 }
 
 /**
@@ -56,6 +57,7 @@ output(ostream &out) const {
  */
 void MemoryUsagePointerCounts::
 output_bytes(ostream &out, size_t size) {
+#ifdef DO_MEMORY_USAGE
   if (size < 4 * 1024) {
     out << size << " bytes";
 
@@ -65,6 +67,5 @@ output_bytes(ostream &out, size_t size) {
   } else {
     out << size / (1024 * 1024) << " Mb";
   }
+#endif
 }
-
-#endif  // DO_MEMORY_USAGE

+ 0 - 4
panda/src/express/memoryUsagePointerCounts.h

@@ -16,8 +16,6 @@
 
 #include "pandabase.h"
 
-#ifdef DO_MEMORY_USAGE
-
 class MemoryInfo;
 
 /**
@@ -55,6 +53,4 @@ INLINE ostream &operator << (ostream &out, const MemoryUsagePointerCounts &c);
 
 #include "memoryUsagePointerCounts.I"
 
-#endif  // DO_MEMORY_USAGE
-
 #endif

+ 33 - 11
panda/src/express/memoryUsagePointers.cxx

@@ -12,9 +12,6 @@
  */
 
 #include "memoryUsagePointers.h"
-
-#ifdef DO_MEMORY_USAGE
-
 #include "config_express.h"
 #include "referenceCount.h"
 #include "typedReferenceCount.h"
@@ -38,7 +35,11 @@ MemoryUsagePointers::
  */
 size_t MemoryUsagePointers::
 get_num_pointers() const {
+#ifdef DO_MEMORY_USAGE
   return _entries.size();
+#else
+  return 0;
+#endif
 }
 
 /**
@@ -46,21 +47,26 @@ get_num_pointers() const {
  */
 ReferenceCount *MemoryUsagePointers::
 get_pointer(size_t n) const {
-  nassertr(n < get_num_pointers(), NULL);
+#ifdef DO_MEMORY_USAGE
+  nassertr(n < get_num_pointers(), nullptr);
   return _entries[n]._ref_ptr;
+#else
+  return nullptr;
+#endif
 }
 
 /**
  * Returns the nth pointer of the set, typecast to a TypedObject if possible.
  * If the pointer is not a TypedObject or if the cast cannot be made, returns
- * NULL.
+ * nullptr.
  */
 TypedObject *MemoryUsagePointers::
 get_typed_pointer(size_t n) const {
-  nassertr(n < get_num_pointers(), NULL);
+#ifdef DO_MEMORY_USAGE
+  nassertr(n < get_num_pointers(), nullptr);
   TypedObject *typed_ptr = _entries[n]._typed_ptr;
 
-  if (typed_ptr != (TypedObject *)NULL) {
+  if (typed_ptr != nullptr) {
     return typed_ptr;
   }
 
@@ -85,7 +91,8 @@ get_typed_pointer(size_t n) const {
       type.is_derived_from(TypedReferenceCount::get_class_type())) {
     return (TypedReferenceCount *)ref_ptr;
   }
-  return NULL;
+#endif
+  return nullptr;
 }
 
 /**
@@ -93,8 +100,12 @@ get_typed_pointer(size_t n) const {
  */
 TypeHandle MemoryUsagePointers::
 get_type(size_t n) const {
+#ifdef DO_MEMORY_USAGE
   nassertr(n < get_num_pointers(), TypeHandle::none());
   return _entries[n]._type;
+#else
+  return TypeHandle::none();
+#endif
 }
 
 /**
@@ -102,8 +113,12 @@ get_type(size_t n) const {
  */
 string MemoryUsagePointers::
 get_type_name(size_t n) const {
+#ifdef DO_MEMORY_USAGE
   nassertr(n < get_num_pointers(), "");
   return get_type(n).get_name();
+#else
+  return "";
+#endif
 }
 
 /**
@@ -113,8 +128,12 @@ get_type_name(size_t n) const {
  */
 double MemoryUsagePointers::
 get_age(size_t n) const {
+#ifdef DO_MEMORY_USAGE
   nassertr(n < get_num_pointers(), 0.0);
   return _entries[n]._age;
+#else
+  return 0.0;
+#endif
 }
 
 /**
@@ -122,7 +141,9 @@ get_age(size_t n) const {
  */
 void MemoryUsagePointers::
 clear() {
+#ifdef DO_MEMORY_USAGE
   _entries.clear();
+#endif
 }
 
 /**
@@ -130,7 +151,9 @@ clear() {
  */
 void MemoryUsagePointers::
 output(ostream &out) const {
+#ifdef DO_MEMORY_USAGE
   out << _entries.size() << " pointers.";
+#endif
 }
 
 /**
@@ -139,13 +162,12 @@ output(ostream &out) const {
 void MemoryUsagePointers::
 add_entry(ReferenceCount *ref_ptr, TypedObject *typed_ptr,
           TypeHandle type, double age) {
+#ifdef DO_MEMORY_USAGE
   // We can't safely add pointers with a zero reference count.  They might be
   // statically-allocated or something, and if we try to add them they'll try
   // to destruct when the PointerTo later goes away.
   if (ref_ptr->get_ref_count() != 0) {
     _entries.push_back(Entry(ref_ptr, typed_ptr, type, age));
   }
+#endif
 }
-
-
-#endif  // DO_MEMORY_USAGE

+ 2 - 5
panda/src/express/memoryUsagePointers.h

@@ -15,9 +15,6 @@
 #define MEMORYUSAGEPOINTERS_H
 
 #include "pandabase.h"
-
-#ifdef DO_MEMORY_USAGE
-
 #include "typedObject.h"
 #include "pointerTo.h"
 #include "referenceCount.h"
@@ -53,7 +50,9 @@ PUBLISHED:
   string get_type_name(size_t n) const;
   double get_age(size_t n) const;
 
+#ifdef DO_MEMORY_USAGE
   EXTENSION(PyObject *get_python_pointer(size_t n) const);
+#endif
 
   void clear();
 
@@ -94,6 +93,4 @@ INLINE ostream &operator << (ostream &out, const MemoryUsagePointers &mup) {
 
 #include "memoryUsagePointers.I"
 
-#endif  // MEMORY_USAGE_POINTERS
-
 #endif

+ 1 - 0
panda/src/express/multifile.h

@@ -136,6 +136,7 @@ PUBLISHED:
   void ls(ostream &out = cout) const;
 
   static INLINE string get_magic_number();
+  MAKE_PROPERTY(magic_number, get_magic_number);
 
   void set_header_prefix(const string &header_prefix);
   INLINE const string &get_header_prefix() const;

+ 98 - 0
panda/src/express/ordered_vector.I

@@ -109,6 +109,44 @@ rend() const {
   return _vector.rend();
 }
 
+/**
+ * Returns the iterator that marks the first element in the ordered vector.
+ */
+template<class Key, class Compare, class Vector>
+INLINE TYPENAME ordered_vector<Key, Compare, Vector>::CONST_ITERATOR ordered_vector<Key, Compare, Vector>::
+cbegin() const {
+  return _vector.begin();
+}
+
+/**
+ * Returns the iterator that marks the end of the ordered vector.
+ */
+template<class Key, class Compare, class Vector>
+INLINE TYPENAME ordered_vector<Key, Compare, Vector>::CONST_ITERATOR ordered_vector<Key, Compare, Vector>::
+cend() const {
+  return _vector.end();
+}
+
+/**
+ * Returns the iterator that marks the first element in the ordered vector,
+ * when viewed in reverse order.
+ */
+template<class Key, class Compare, class Vector>
+INLINE TYPENAME ordered_vector<Key, Compare, Vector>::CONST_REVERSE_ITERATOR ordered_vector<Key, Compare, Vector>::
+crbegin() const {
+  return _vector.rbegin();
+}
+
+/**
+ * Returns the iterator that marks the end of the ordered vector, when viewed
+ * in reverse order.
+ */
+template<class Key, class Compare, class Vector>
+INLINE TYPENAME ordered_vector<Key, Compare, Vector>::CONST_REVERSE_ITERATOR ordered_vector<Key, Compare, Vector>::
+crend() const {
+  return _vector.rend();
+}
+
 /**
  * Returns the nth element.
  */
@@ -127,6 +165,54 @@ operator [] (TYPENAME ordered_vector<Key, Compare, Vector>::SIZE_TYPE n) const {
   return _vector[n];
 }
 
+/**
+ * Returns a reference to the first element.
+ */
+template<class Key, class Compare, class Vector>
+INLINE TYPENAME ordered_vector<Key, Compare, Vector>::REFERENCE ordered_vector<Key, Compare, Vector>::
+front() {
+#ifdef _DEBUG
+  assert(!_vector.empty());
+#endif
+  return _vector[0];
+}
+
+/**
+ * Returns a const reference to the first element.
+ */
+template<class Key, class Compare, class Vector>
+INLINE TYPENAME ordered_vector<Key, Compare, Vector>::CONST_REFERENCE ordered_vector<Key, Compare, Vector>::
+front() const {
+#ifdef _DEBUG
+  assert(!_vector.empty());
+#endif
+  return _vector[0];
+}
+
+/**
+ * Returns a reference to the first element.
+ */
+template<class Key, class Compare, class Vector>
+INLINE TYPENAME ordered_vector<Key, Compare, Vector>::REFERENCE ordered_vector<Key, Compare, Vector>::
+back() {
+#ifdef _DEBUG
+  assert(!_vector.empty());
+#endif
+  return _vector[_vector.size() - 1];
+}
+
+/**
+ * Returns a const reference to the last element.
+ */
+template<class Key, class Compare, class Vector>
+INLINE TYPENAME ordered_vector<Key, Compare, Vector>::CONST_REFERENCE ordered_vector<Key, Compare, Vector>::
+back() const {
+#ifdef _DEBUG
+  assert(!_vector.empty());
+#endif
+  return _vector[_vector.size() - 1];
+}
+
 /**
  * Returns the number of elements in the ordered vector.
  */
@@ -530,6 +616,18 @@ push_back(const value_type &key) {
   _vector.push_back(key);
 }
 
+/**
+ * Adds the new element to the end of the vector without regard for proper
+ * sorting.  This is a bad idea to do except to populate the vector the first
+ * time; be sure to call sort() after you have added all the elements.
+ */
+template<class Key, class Compare, class Vector>
+INLINE void ordered_vector<Key, Compare, Vector>::
+push_back(value_type &&key) {
+  TAU_PROFILE("ordered_vector::push_back()", " ", TAU_USER);
+  _vector.push_back(move(key));
+}
+
 /**
  * Removes the last element at the end of the vector.
  */

+ 12 - 0
panda/src/express/ordered_vector.h

@@ -147,10 +147,21 @@ public:
   INLINE CONST_REVERSE_ITERATOR rbegin() const;
   INLINE CONST_REVERSE_ITERATOR rend() const;
 
+  INLINE CONST_ITERATOR cbegin() const;
+  INLINE CONST_ITERATOR cend() const;
+  INLINE CONST_REVERSE_ITERATOR crbegin() const;
+  INLINE CONST_REVERSE_ITERATOR crend() const;
+
   // Random access.
   INLINE reference operator [] (SIZE_TYPE n);
   INLINE const_reference operator [] (SIZE_TYPE n) const;
 
+  INLINE reference front();
+  INLINE const_reference front() const;
+
+  INLINE reference back();
+  INLINE const_reference back() const;
+
   // Size information.
   INLINE SIZE_TYPE size() const;
   INLINE SIZE_TYPE max_size() const;
@@ -201,6 +212,7 @@ public:
   bool verify_list_nonunique() const;
 
   INLINE void push_back(const VALUE_TYPE &key);
+  INLINE void push_back(VALUE_TYPE &&key);
   INLINE void pop_back();
   INLINE void resize(SIZE_TYPE n);
   INLINE void resize(SIZE_TYPE n, const VALUE_TYPE &value);

+ 2 - 2
panda/src/express/pointerTo.h

@@ -70,7 +70,7 @@ class PointerTo : public PointerToBase<T> {
 public:
   typedef TYPENAME PointerToBase<T>::To To;
 PUBLISHED:
-  ALWAYS_INLINE CONSTEXPR PointerTo() NOEXCEPT DEFAULT_CTOR;
+  ALWAYS_INLINE_CONSTEXPR PointerTo() NOEXCEPT DEFAULT_CTOR;
   ALWAYS_INLINE PointerTo(To *ptr) NOEXCEPT;
   INLINE PointerTo(const PointerTo<T> &copy);
 
@@ -133,7 +133,7 @@ class ConstPointerTo : public PointerToBase<T> {
 public:
   typedef TYPENAME PointerToBase<T>::To To;
 PUBLISHED:
-  ALWAYS_INLINE CONSTEXPR ConstPointerTo() NOEXCEPT DEFAULT_CTOR;
+  ALWAYS_INLINE_CONSTEXPR ConstPointerTo() NOEXCEPT DEFAULT_CTOR;
   ALWAYS_INLINE ConstPointerTo(const To *ptr) NOEXCEPT;
   INLINE ConstPointerTo(const PointerTo<T> &copy);
   INLINE ConstPointerTo(const ConstPointerTo<T> &copy);

+ 2 - 3
panda/src/express/pointerToBase.I

@@ -141,7 +141,6 @@ reassign(const PointerToBase<To> &copy) {
   }
 }
 
-#ifdef DO_MEMORY_USAGE
 /**
  * Ensures that the MemoryUsage record for the pointer has the right type of
  * object, if we know the type ourselves.
@@ -149,6 +148,7 @@ reassign(const PointerToBase<To> &copy) {
 template<class T>
 INLINE void PointerToBase<T>::
 update_type(To *ptr) {
+#ifdef DO_MEMORY_USAGE
   if (MemoryUsage::get_track_memory_usage()) {
     TypeHandle type = get_type_handle(To);
     if (type == TypeHandle::none()) {
@@ -159,9 +159,8 @@ update_type(To *ptr) {
       MemoryUsage::update_type(ptr, type);
     }
   }
-}
 #endif  // DO_MEMORY_USAGE
-
+}
 
 /**
  * A convenient way to set the PointerTo object to NULL. (Assignment to a NULL

+ 1 - 3
panda/src/express/pointerToBase.h

@@ -31,7 +31,7 @@ public:
   typedef T To;
 
 protected:
-  ALWAYS_INLINE CONSTEXPR PointerToBase() NOEXCEPT DEFAULT_CTOR;
+  ALWAYS_INLINE_CONSTEXPR PointerToBase() NOEXCEPT DEFAULT_CTOR;
   INLINE PointerToBase(To *ptr);
   INLINE PointerToBase(const PointerToBase<T> &copy);
   INLINE ~PointerToBase();
@@ -44,9 +44,7 @@ protected:
   INLINE void reassign(To *ptr);
   INLINE void reassign(const PointerToBase<To> &copy);
 
-#ifdef DO_MEMORY_USAGE
   INLINE void update_type(To *ptr);
-#endif  // DO_MEMORY_USAGE
 
   // No assignment or retrieval functions are declared in PointerToBase,
   // because we will have to specialize on const vs.  non-const later.

+ 2 - 3
panda/src/express/threadSafePointerToBase.I

@@ -89,7 +89,6 @@ reassign(const ThreadSafePointerToBase<To> &copy) {
   reassign((To *)copy._void_ptr);
 }
 
-#ifdef DO_MEMORY_USAGE
 /**
  * Ensures that the MemoryUsage record for the pointer has the right type of
  * object, if we know the type ourselves.
@@ -97,6 +96,7 @@ reassign(const ThreadSafePointerToBase<To> &copy) {
 template<class T>
 void ThreadSafePointerToBase<T>::
 update_type(To *ptr) {
+#ifdef DO_MEMORY_USAGE
   TypeHandle type = get_type_handle(To);
   if (type == TypeHandle::none()) {
     do_init_type(To);
@@ -105,9 +105,8 @@ update_type(To *ptr) {
   if (type != TypeHandle::none()) {
     MemoryUsage::update_type(ptr, type);
   }
-}
 #endif  // DO_MEMORY_USAGE
-
+}
 
 /**
  * A convenient way to set the ThreadSafePointerTo object to NULL. (Assignment

+ 0 - 2
panda/src/express/threadSafePointerToBase.h

@@ -40,9 +40,7 @@ protected:
   INLINE void reassign(To *ptr);
   INLINE void reassign(const ThreadSafePointerToBase<To> &copy);
 
-#ifdef DO_MEMORY_USAGE
   void update_type(To *ptr);
-#endif  // DO_MEMORY_USAGE
 
   // No assignment or retrieval functions are declared in
   // ThreadSafePointerToBase, because we will have to specialize on const vs.

+ 9 - 0
panda/src/express/virtualFileSimple.cxx

@@ -150,7 +150,16 @@ copy_file(VirtualFile *new_file) {
   // Different mount point, or the mount doesn't support copying.  Do it by
   // hand.
   ostream *out = new_file->open_write_file(false, true);
+  if (out == nullptr) {
+    return false;
+  }
+
   istream *in = open_read_file(false);
+  if (in == nullptr) {
+    new_file->close_write_file(out);
+    new_file->delete_file();
+    return false;
+  }
 
   static const size_t buffer_size = 4096;
   char buffer[buffer_size];

+ 18 - 0
panda/src/express/virtualFileSystem.cxx

@@ -1226,6 +1226,24 @@ do_get_file(const Filename &filename, int open_flags) const {
     }
   }
 
+#if defined(_WIN32) && !defined(NDEBUG)
+  if (!found_file) {
+    // The file could not be found.  Perhaps this is because the user passed
+    // in a Windows-style path where a Unix-style path was expected?
+    if (filename.length() > 2 && isalpha(filename[0]) && filename[1] == ':' &&
+        (filename[2] == '\\' || filename[2] == '/')) {
+
+      Filename corrected_fn = Filename::from_os_specific(filename);
+      if (corrected_fn.exists()) {
+        express_cat.warning()
+          << "Filename uses Windows-style path: " << filename << "\n";
+        express_cat.warning()
+          << "  expected Unix-style path: " << corrected_fn << "\n";
+      }
+    }
+  }
+#endif
+
   return found_file;
 }
 

+ 0 - 34
panda/src/glstuff/glCgShaderContext_src.I

@@ -10,37 +10,3 @@
  * @author rdb
  * @date 2014-06-27
  */
-
-#ifndef OPENGLES_1
-
-/**
- * Returns true if the shader is "valid", ie, if the compilation was
- * successful.  The compilation could fail if there is a syntax error in the
- * shader, or if the current video card isn't shader-capable, or if no shader
- * languages are compiled into panda.
- */
-INLINE bool CLP(CgShaderContext)::
-valid() {
-  if (_shader->get_error_flag()) return false;
-  if (_shader->get_language() != Shader::SL_Cg) return false;
-  return (_cg_program != 0);
-}
-
-/**
- * Returns true if the shader may need to access standard vertex attributes as
- * passed by glVertexPointer and the like.
- */
-INLINE bool CLP(CgShaderContext)::
-uses_standard_vertex_arrays() {
-  return false;
-}
-
-/**
- * Always true, for now.
- */
-INLINE bool CLP(CgShaderContext)::
-uses_custom_vertex_arrays() {
-  return true;
-}
-
-#endif  // OPENGLES_1

+ 14 - 0
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -357,6 +357,20 @@ release_resources() {
   _glgsg->report_my_gl_errors();
 }
 
+/**
+ * Returns true if the shader is "valid", ie, if the compilation was
+ * successful.  The compilation could fail if there is a syntax error in the
+ * shader, or if the current video card isn't shader-capable, or if no shader
+ * languages are compiled into panda.
+ */
+bool CLP(CgShaderContext)::
+valid() {
+  if (_shader == nullptr || _shader->get_error_flag()) {
+    return false;
+  }
+  return (_cg_program != 0);
+}
+
 /**
  * This function is to be called to enable a new shader.  It also initializes
  * all of the shader's input parameters.

+ 13 - 13
panda/src/glstuff/glCgShaderContext_src.h

@@ -33,25 +33,25 @@ public:
   ~CLP(CgShaderContext)();
   ALLOC_DELETED_CHAIN(CLP(CgShaderContext));
 
-  INLINE bool valid(void);
-  void bind() OVERRIDE;
-  void unbind() OVERRIDE;
+  bool valid(void) override;
+  void bind() override;
+  void unbind() override;
 
   void set_state_and_transform(const RenderState *state,
                                const TransformState *modelview_transform,
                                const TransformState *camera_transform,
-                               const TransformState *projection_transform) OVERRIDE;
+                               const TransformState *projection_transform) override;
 
-  void issue_parameters(int altered) OVERRIDE;
+  void issue_parameters(int altered) override;
   void update_transform_table(const TransformTable *table);
   void update_slider_table(const SliderTable *table);
-  void disable_shader_vertex_arrays() OVERRIDE;
-  bool update_shader_vertex_arrays(ShaderContext *prev, bool force) OVERRIDE;
-  void disable_shader_texture_bindings() OVERRIDE;
-  void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE;
+  void disable_shader_vertex_arrays() override;
+  bool update_shader_vertex_arrays(ShaderContext *prev, bool force) override;
+  void disable_shader_texture_bindings() override;
+  void update_shader_texture_bindings(ShaderContext *prev) override;
 
-  INLINE bool uses_standard_vertex_arrays(void);
-  INLINE bool uses_custom_vertex_arrays(void);
+  bool uses_standard_vertex_arrays(void) override { return false; }
+  bool uses_custom_vertex_arrays(void) override { return true; }
 
   // Special values for location to indicate conventional attrib slots.
   enum ConventionalAttrib {
@@ -95,10 +95,10 @@ public:
     register_type(_type_handle, CLASSPREFIX_QUOTED "CgShaderContext",
                   ShaderContext::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {init_type(); return get_class_type();}
+  virtual TypeHandle force_init_type() override {init_type(); return get_class_type();}
 
 private:
   static TypeHandle _type_handle;

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

@@ -2135,7 +2135,7 @@ reset() {
     _glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)
       get_extension_func("glGenerateMipmap");
     _glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
-      get_extension_func("glRenderbufferStorageMultisampleEXT");
+      get_extension_func("glRenderbufferStorageMultisample");
     _glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)
       get_extension_func("glBlitFramebuffer");
 
@@ -3934,7 +3934,6 @@ end_frame(Thread *current_thread) {
  */
 bool CLP(GraphicsStateGuardian)::
 begin_draw_primitives(const GeomPipelineReader *geom_reader,
-                      const GeomMunger *munger,
                       const GeomVertexDataPipelineReader *data_reader,
                       bool force) {
 #ifndef NDEBUG
@@ -3953,7 +3952,7 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
   }
 #endif
 
-  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, data_reader, force)) {
+  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, data_reader, force)) {
     return false;
   }
   nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false);
@@ -4018,10 +4017,10 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
     GeomContext *gc = geom_reader->prepare_now(get_prepared_objects(), this);
     nassertr(gc != (GeomContext *)NULL, false);
     CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc);
-    const CLP(GeomMunger) *gmunger = DCAST(CLP(GeomMunger), _munger);
+    //const CLP(GeomMunger) *gmunger = DCAST(CLP(GeomMunger), _munger);
 
     UpdateSeq modified = max(geom_reader->get_modified(), _data_reader->get_modified());
-    if (ggc->get_display_list(_geom_display_list, gmunger, modified)) {
+    if (ggc->get_display_list(_geom_display_list, nullptr, modified)) {
       // If it hasn't been modified, just play the display list again.
       if (GLCAT.is_spam()) {
         GLCAT.spam()

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

@@ -284,7 +284,6 @@ public:
   virtual void end_frame(Thread *current_thread);
 
   virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader,
-                                     const GeomMunger *munger,
                                      const GeomVertexDataPipelineReader *data_reader,
                                      bool force);
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader,

+ 0 - 35
panda/src/glstuff/glShaderContext_src.I

@@ -10,38 +10,3 @@
  * @author jyelon
  * @date 2005-09-01
  */
-
-/**
- * Returns true if the shader is "valid", ie, if the compilation was
- * successful.  The compilation could fail if there is a syntax error in the
- * shader, or if the current video card isn't shader-capable, or if no shader
- * languages are compiled into panda.
- */
-INLINE bool CLP(ShaderContext)::
-valid() {
-  if (_shader->get_error_flag()) return false;
-  if (_shader->get_language() != Shader::SL_GLSL) {
-    return false;
-  }
-  if (_glsl_program != 0) {
-    return true;
-  }
-  return false;
-}
-
-/**
- * Returns true if the shader may need to access standard vertex attributes as
- * passed by glVertexPointer and the like.
- */
-INLINE bool CLP(ShaderContext)::
-uses_standard_vertex_arrays() {
-  return _uses_standard_vertex_arrays;
-}
-
-/**
- * Always true, for now.
- */
-INLINE bool CLP(ShaderContext)::
-uses_custom_vertex_arrays() {
-  return true;
-}

+ 19 - 5
panda/src/glstuff/glShaderContext_src.cxx

@@ -1431,7 +1431,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
             bind._arg[0] = NULL;
             bind._dep[0] = Shader::SSD_general | Shader::SSD_transform;
             bind._part[1] = Shader::SMO_mat_constant_x_attrib;
-            bind._arg[1] = InternalName::make("shadowViewMatrix");
+            bind._arg[1] = iname->get_parent()->append("shadowViewMatrix");
             bind._dep[1] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform;
           } else {
             bind._part[0] = Shader::SMO_mat_constant_x_attrib;
@@ -1821,6 +1821,20 @@ release_resources() {
   _glgsg->report_my_gl_errors();
 }
 
+/**
+ * Returns true if the shader is "valid", ie, if the compilation was
+ * successful.  The compilation could fail if there is a syntax error in the
+ * shader, or if the current video card isn't shader-capable, or if no shader
+ * languages are compiled into panda.
+ */
+bool CLP(ShaderContext)::
+valid() {
+  if (_shader->get_error_flag()) {
+    return false;
+  }
+  return (_glsl_program != 0);
+}
+
 /**
  * This function is to be called to enable a new shader.  It also initializes
  * all of the shader's input parameters.
@@ -2177,7 +2191,7 @@ update_slider_table(const SliderTable *table) {
  */
 void CLP(ShaderContext)::
 disable_shader_vertex_arrays() {
-  if (!valid()) {
+  if (_glsl_program == 0) {
     return;
   }
 
@@ -2200,7 +2214,7 @@ disable_shader_vertex_arrays() {
  */
 bool CLP(ShaderContext)::
 update_shader_vertex_arrays(ShaderContext *prev, bool force) {
-  if (!valid()) {
+  if (_glsl_program == 0) {
     return true;
   }
 
@@ -2373,7 +2387,7 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
  */
 void CLP(ShaderContext)::
 disable_shader_texture_bindings() {
-  if (!valid()) {
+  if (_glsl_program == 0) {
     return;
   }
 
@@ -2473,7 +2487,7 @@ void CLP(ShaderContext)::
 update_shader_texture_bindings(ShaderContext *prev) {
   // if (prev) { prev->disable_shader_texture_bindings(); }
 
-  if (!valid()) {
+  if (_glsl_program == 0) {
     return;
   }
 

+ 19 - 15
panda/src/glstuff/glShaderContext_src.h

@@ -40,26 +40,30 @@ public:
   void reflect_uniform(int i, char *name_buffer, GLsizei name_buflen);
   bool get_sampler_texture_type(int &out, GLenum param_type);
 
-  INLINE bool valid(void);
-  void bind() OVERRIDE;
-  void unbind() OVERRIDE;
+  bool valid(void) override;
+  void bind() override;
+  void unbind() override;
 
   void set_state_and_transform(const RenderState *state,
                                const TransformState *modelview_transform,
                                const TransformState *camera_transform,
-                               const TransformState *projection_transform) OVERRIDE;
+                               const TransformState *projection_transform) override;
 
-  void issue_parameters(int altered) OVERRIDE;
+  void issue_parameters(int altered) override;
   void update_transform_table(const TransformTable *table);
   void update_slider_table(const SliderTable *table);
-  void disable_shader_vertex_arrays() OVERRIDE;
-  bool update_shader_vertex_arrays(ShaderContext *prev, bool force) OVERRIDE;
-  void disable_shader_texture_bindings() OVERRIDE;
-  void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE;
-  void update_shader_buffer_bindings(ShaderContext *prev) OVERRIDE;
-
-  INLINE bool uses_standard_vertex_arrays(void);
-  INLINE bool uses_custom_vertex_arrays(void);
+  void disable_shader_vertex_arrays() override;
+  bool update_shader_vertex_arrays(ShaderContext *prev, bool force) override;
+  void disable_shader_texture_bindings() override;
+  void update_shader_texture_bindings(ShaderContext *prev) override;
+  void update_shader_buffer_bindings(ShaderContext *prev) override;
+
+  bool uses_standard_vertex_arrays(void) override {
+    return _uses_standard_vertex_arrays;
+  }
+  bool uses_custom_vertex_arrays(void) override {
+    return true;
+  }
 
 private:
   bool _validated;
@@ -128,10 +132,10 @@ public:
     register_type(_type_handle, CLASSPREFIX_QUOTED "ShaderContext",
                   ShaderContext::get_class_type());
   }
-  virtual TypeHandle get_type() const OVERRIDE {
+  virtual TypeHandle get_type() const override {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() OVERRIDE {init_type(); return get_class_type();}
+  virtual TypeHandle force_init_type() override {init_type(); return get_class_type();}
 
 private:
   static TypeHandle _type_handle;

+ 6 - 6
panda/src/gobj/geom.cxx

@@ -683,6 +683,7 @@ unify_in_place(int max_indices, bool preserve_order) {
           int num_vertices = copy_primitives * total_vertices_per_primitive;
           nassertv(num_vertices > 0);
           {
+            smaller->set_index_type(reader.get_index_type());
             GeomVertexArrayDataHandle writer(smaller->modify_vertices(), current_thread);
             writer.unclean_set_num_rows(num_vertices);
             memcpy(writer.get_write_pointer(), ptr, stride * (size_t)(num_vertices - num_unused_vertices_per_primitive));
@@ -1211,14 +1212,13 @@ prepare_now(PreparedGraphicsObjects *prepared_objects,
  * is passed true, it will wait for the data to become resident if necessary.
  */
 bool Geom::
-draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
-     const GeomVertexData *vertex_data, bool force,
-     Thread *current_thread) const {
+draw(GraphicsStateGuardianBase *gsg, const GeomVertexData *vertex_data,
+     bool force, Thread *current_thread) const {
   GeomPipelineReader geom_reader(this, current_thread);
   GeomVertexDataPipelineReader data_reader(vertex_data, current_thread);
   data_reader.check_array_readers();
 
-  return geom_reader.draw(gsg, munger, &data_reader, force);
+  return geom_reader.draw(gsg, &data_reader, force);
 }
 
 /**
@@ -1749,10 +1749,10 @@ check_valid(const GeomVertexDataPipelineReader *data_reader) const {
  * The implementation of Geom::draw().
  */
 bool GeomPipelineReader::
-draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
+draw(GraphicsStateGuardianBase *gsg,
      const GeomVertexDataPipelineReader *data_reader, bool force) const {
   PStatTimer timer(Geom::_draw_primitive_setup_pcollector);
-  bool all_ok = gsg->begin_draw_primitives(this, munger, data_reader, force);
+  bool all_ok = gsg->begin_draw_primitives(this, data_reader, force);
   if (all_ok) {
     Geom::Primitives::const_iterator pi;
     for (pi = _cdata->_primitives.begin();

+ 1 - 2
panda/src/gobj/geom.h

@@ -153,7 +153,6 @@ PUBLISHED:
 
 public:
   bool draw(GraphicsStateGuardianBase *gsg,
-            const GeomMunger *munger,
             const GeomVertexData *vertex_data,
             bool force, Thread *current_thread) const;
 
@@ -429,7 +428,7 @@ public:
   INLINE GeomContext *prepare_now(PreparedGraphicsObjects *prepared_objects,
                                   GraphicsStateGuardianBase *gsg) const;
 
-  bool draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
+  bool draw(GraphicsStateGuardianBase *gsg,
             const GeomVertexDataPipelineReader *data_reader,
             bool force) const;
 

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

@@ -178,11 +178,9 @@ private:
   static TypeHandle _texcoord_type_handle;
 };
 
-#ifdef DO_MEMORY_USAGE
 // We can safely redefine this as a no-op.
 template<>
 INLINE void PointerToBase<InternalName>::update_type(To *ptr) {}
-#endif
 
 INLINE ostream &operator << (ostream &out, const InternalName &tcn);
 

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

@@ -327,3 +327,11 @@ INLINE void Material::
 set_attrib_lock() {
   _flags |= F_attrib_lock;
 }
+
+/**
+ *
+ */
+INLINE int Material::
+get_flags() const {
+  return _flags;
+}

+ 2 - 2
panda/src/gobj/material.h

@@ -128,6 +128,8 @@ PUBLISHED:
   MAKE_PROPERTY(twoside, get_twoside, set_twoside);
 
 public:
+  INLINE int get_flags() const;
+
   enum Flags {
     F_ambient     = 0x001,
     F_diffuse     = 0x002,
@@ -157,8 +159,6 @@ private:
 
   int _flags;
 
-  friend class MaterialAttrib;
-
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);

+ 109 - 8
panda/src/gobj/shader.cxx

@@ -450,9 +450,11 @@ cp_dependency(ShaderMatInput inp) {
     }
   }
   if ((inp == SMO_light_ambient) ||
-      (inp == SMO_light_source_i_attrib)) {
-    dep |= SSD_light;
-    if (inp == SMO_light_source_i_attrib) {
+      (inp == SMO_light_source_i_attrib) ||
+      (inp == SMO_light_source_i_packed)) {
+    dep |= SSD_light | SSD_frame;
+    if (inp == SMO_light_source_i_attrib ||
+        inp == SMO_light_source_i_packed) {
       dep |= SSD_view_transform;
     }
   }
@@ -465,7 +467,7 @@ cp_dependency(ShaderMatInput inp) {
       (inp == SMO_apiview_clipplane_i)) {
     dep |= SSD_clip_planes;
   }
-  if (inp == SMO_texmat_i || inp == SMO_inv_texmat_i) {
+  if (inp == SMO_texmat_i || inp == SMO_inv_texmat_i || inp == SMO_texscale_i) {
     dep |= SSD_tex_matrix;
   }
   if ((inp == SMO_window_size) ||
@@ -483,7 +485,7 @@ cp_dependency(ShaderMatInput inp) {
       (inp == SMO_apiclip_to_apiview)) {
     dep |= SSD_projection;
   }
-  if (inp == SMO_tex_is_alpha_i) {
+  if (inp == SMO_tex_is_alpha_i || inp == SMO_texcolor_i) {
     dep |= SSD_texture | SSD_frame;
   }
 
@@ -734,6 +736,29 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) {
     return true;
   }
 
+  if (pieces[0] == "mat" && pieces[1] == "shadow") {
+    if ((!cp_errchk_parameter_words(p,3))||
+        (!cp_errchk_parameter_in(p)) ||
+        (!cp_errchk_parameter_uniform(p))||
+        (!cp_errchk_parameter_float(p,16,16))) {
+      return false;
+    }
+    ShaderMatSpec bind;
+    bind._id = p._id;
+    bind._piece = SMP_whole;
+    bind._func = SMF_compose;
+    bind._part[1] = SMO_light_source_i_attrib;
+    bind._arg[1] = InternalName::make("shadowViewMatrix");
+    bind._part[0] = SMO_view_to_apiview;
+    bind._arg[0] = NULL;
+    bind._index = atoi(pieces[2].c_str());
+
+    cp_optimize_mat_spec(bind);
+    _mat_spec.push_back(bind);
+    _mat_deps |= bind._dep[0] | bind._dep[1];
+    return true;
+  }
+
   // Implement some macros.  Macros work by altering the contents of the
   // 'pieces' array, and then falling through.
 
@@ -833,7 +858,13 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) {
 
     ShaderMatSpec bind;
     bind._id = p._id;
+    bind._piece = SMP_whole;
     bind._func = SMF_compose;
+    bind._part[1] = SMO_light_source_i_attrib;
+    bind._arg[1] = InternalName::make("shadowViewMatrix");
+    bind._part[0] = SMO_view_to_apiview;
+    bind._arg[0] = NULL;
+    bind._index = atoi(pieces[2].c_str());
 
     int next = 1;
     pieces.push_back("");
@@ -958,6 +989,30 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) {
       bind._arg[0] = NULL;
       bind._part[1] = SMO_identity;
       bind._arg[1] = NULL;
+    } else if (pieces[1].compare(0, 5, "light") == 0) {
+      if (!cp_errchk_parameter_float(p,16,16)) {
+        return false;
+      }
+      bind._id = p._id;
+      bind._piece = SMP_transpose;
+      bind._func = SMF_first;
+      bind._part[0] = SMO_light_source_i_packed;
+      bind._arg[0] = NULL;
+      bind._part[1] = SMO_identity;
+      bind._arg[1] = NULL;
+      bind._index = atoi(pieces[1].c_str() + 5);
+    } else if (pieces[1].compare(0, 5, "lspec") == 0) {
+      if (!cp_errchk_parameter_float(p,3,4)) {
+        return false;
+      }
+      bind._id = p._id;
+      bind._piece = SMP_row3;
+      bind._func = SMF_first;
+      bind._part[0] = SMO_light_source_i_attrib;
+      bind._arg[0] = InternalName::make("specular");
+      bind._part[1] = SMO_identity;
+      bind._arg[1] = NULL;
+      bind._index = atoi(pieces[1].c_str() + 5);
     } else {
       cp_report_error(p,"Unknown attr parameter.");
       return false;
@@ -1094,6 +1149,52 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) {
     return true;
   }
 
+  if (pieces[0] == "texscale") {
+    if ((!cp_errchk_parameter_words(p,2))||
+        (!cp_errchk_parameter_in(p)) ||
+        (!cp_errchk_parameter_uniform(p))||
+        (!cp_errchk_parameter_float(p,3,4))) {
+      return false;
+    }
+    ShaderMatSpec bind;
+    bind._id = p._id;
+    bind._piece = SMP_row3;
+    bind._func = SMF_first;
+    bind._part[0] = SMO_texscale_i;
+    bind._arg[0] = NULL;
+    bind._part[1] = SMO_identity;
+    bind._arg[1] = NULL;
+    bind._index = atoi(pieces[1].c_str());
+
+    cp_optimize_mat_spec(bind);
+    _mat_spec.push_back(bind);
+    _mat_deps |= bind._dep[0] | bind._dep[1];
+    return true;
+  }
+
+  if (pieces[0] == "texcolor") {
+    if ((!cp_errchk_parameter_words(p,2))||
+        (!cp_errchk_parameter_in(p)) ||
+        (!cp_errchk_parameter_uniform(p))||
+        (!cp_errchk_parameter_float(p,3,4))) {
+      return false;
+    }
+    ShaderMatSpec bind;
+    bind._id = p._id;
+    bind._piece = SMP_row3;
+    bind._func = SMF_first;
+    bind._part[0] = SMO_texcolor_i;
+    bind._arg[0] = NULL;
+    bind._part[1] = SMO_identity;
+    bind._arg[1] = NULL;
+    bind._index = atoi(pieces[1].c_str());
+
+    cp_optimize_mat_spec(bind);
+    _mat_spec.push_back(bind);
+    _mat_deps |= bind._dep[0] | bind._dep[1];
+    return true;
+  }
+
   if (pieces[0] == "plane") {
     if ((!cp_errchk_parameter_words(p,2))||
         (!cp_errchk_parameter_in(p)) ||
@@ -1233,9 +1334,9 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) {
     }
     ShaderTexSpec bind;
     bind._id = p._id;
-    bind._name = InternalName::make(pieces[1])->append("shadowMap");
-    bind._stage = -1;
-    bind._part = STO_named_input;
+    bind._name = nullptr;
+    bind._stage = atoi(pieces[1].c_str());
+    bind._part = STO_light_i_shadow_map;
     switch (p._type) {
     case SAT_sampler2d:      bind._desired_type = Texture::TT_2d_texture; break;
     case SAT_sampler_cube:   bind._desired_type = Texture::TT_cube_map; break;

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

@@ -202,6 +202,17 @@ public:
     // Hack for text rendering.  Don't use in user shaders.
     SMO_tex_is_alpha_i,
 
+    SMO_transform_i,
+    SMO_slider_i,
+
+    SMO_light_source_i_packed,
+
+    // Texture scale component of texture matrix.
+    SMO_texscale_i,
+
+    // Color of an M_blend texture stage.
+    SMO_texcolor_i,
+
     SMO_INVALID
   };
 

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

@@ -201,6 +201,8 @@ PUBLISHED:
 
   MAKE_PROPERTY(tex_view_offset, get_tex_view_offset, set_tex_view_offset);
 
+  MAKE_PROPERTY(default, get_default);
+
 public:
   INLINE static UpdateSeq get_sort_seq();
 

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

@@ -43,6 +43,7 @@ PUBLISHED:
 
   INLINE static void set_mode(Mode mode);
   INLINE static Mode get_mode();
+  MAKE_PROPERTY(mode, get_mode, set_mode);
 
   INLINE static int garbage_collect();
   INLINE static void list_contents(ostream &out);

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

@@ -64,6 +64,7 @@ PUBLISHED:
   INLINE static SimpleLru *get_global_lru(RamClass rclass);
   INLINE static SimpleLru *get_pending_lru();
   INLINE static VertexDataSaveFile *get_save_file();
+  MAKE_PROPERTY(save_file, get_save_file);
 
   INLINE bool save_to_disk();
 

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

@@ -198,7 +198,6 @@ public:
   // friends of this class.
 
   virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader,
-                                     const GeomMunger *munger,
                                      const GeomVertexDataPipelineReader *data_reader,
                                      bool force)=0;
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, bool force)=0;

+ 0 - 2
panda/src/mathutil/geometricBoundingVolume.h

@@ -83,11 +83,9 @@ private:
   static TypeHandle _type_handle;
 };
 
-#ifdef DO_MEMORY_USAGE
 // We can safely redefine this as a no-op.
 template<>
 INLINE void PointerToBase<GeometricBoundingVolume>::update_type(To *ptr) {}
-#endif
 
 #include "geometricBoundingVolume.I"
 

+ 17 - 0
panda/src/parametrics/ropeNode.h

@@ -136,6 +136,23 @@ PUBLISHED:
 
   void reset_bound(const NodePath &rel_to);
 
+PUBLISHED:
+  MAKE_PROPERTY(curve, get_curve, set_curve);
+  MAKE_PROPERTY(render_mode, get_render_mode, set_render_mode);
+  MAKE_PROPERTY(uv_mode, get_uv_mode, set_uv_mode);
+  MAKE_PROPERTY(uv_direction, get_uv_direction, set_uv_direction);
+  MAKE_PROPERTY(uv_scale, get_uv_scale, set_uv_scale);
+  MAKE_PROPERTY(normal_mode, get_normal_mode, set_normal_mode);
+  MAKE_PROPERTY(tube_up, get_tube_up, set_tube_up);
+  MAKE_PROPERTY(use_vertex_color, get_use_vertex_color, set_use_vertex_color);
+  MAKE_PROPERTY(vertex_color_dimension, get_vertex_color_dimension);
+  MAKE_PROPERTY(num_subdiv, get_num_subdiv, set_num_subdiv);
+  MAKE_PROPERTY(num_slices, get_num_slices, set_num_slices);
+  MAKE_PROPERTY(use_vertex_thickness, get_use_vertex_thickness, set_use_vertex_thickness);
+  MAKE_PROPERTY(vertex_thickness_dimension, get_vertex_thickness_dimension);
+  MAKE_PROPERTY(thickness, get_thickness, set_thickness);
+  MAKE_PROPERTY2(matrix, has_matrix, get_matrix, set_matrix, clear_matrix);
+
 protected:
   virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
                                        int &internal_vertices,

+ 0 - 16
panda/src/pgraph/alphaTestAttrib.cxx

@@ -94,22 +94,6 @@ get_hash_impl() const {
   return hash;
 }
 
-/**
- *
- */
-CPT(RenderAttrib) AlphaTestAttrib::
-get_auto_shader_attrib_impl(const RenderState *state) const {
-  // This is only important if the shader subsumes the alpha test, which only
-  // happens if there is an AuxBitplaneAttrib with ABO_glow.
-  const AuxBitplaneAttrib *aux;
-  if (!state->get_attrib(aux) ||
-      (aux->get_outputs() & AuxBitplaneAttrib::ABO_glow) == 0) {
-    return nullptr;
-  } else {
-    return this;
-  }
-}
-
 /**
  * Tells the BamReader how to create objects of type AlphaTestAttrib.
  */

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

@@ -46,7 +46,6 @@ public:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual size_t get_hash_impl() const;
-  virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
 
 private:
   PandaCompareFunc _mode;
@@ -59,6 +58,7 @@ PUBLISHED:
   virtual int get_slot() const {
     return get_class_slot();
   }
+  MAKE_PROPERTY(class_slot, get_class_slot);
 
 public:
   static void register_with_read_factory();

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

@@ -75,6 +75,7 @@ PUBLISHED:
   virtual int get_slot() const {
     return get_class_slot();
   }
+  MAKE_PROPERTY(class_slot, get_class_slot);
 
 public:
   static void register_with_read_factory();

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

@@ -65,6 +65,7 @@ PUBLISHED:
   virtual int get_slot() const {
     return get_class_slot();
   }
+  MAKE_PROPERTY(class_slot, get_class_slot);
 
 public:
   static void register_with_read_factory();

+ 0 - 8
panda/src/pgraph/auxBitplaneAttrib.cxx

@@ -97,14 +97,6 @@ get_hash_impl() const {
   return hash;
 }
 
-/**
- *
- */
-CPT(RenderAttrib) AuxBitplaneAttrib::
-get_auto_shader_attrib_impl(const RenderState *state) const {
-  return this;
-}
-
 /**
  * Tells the BamReader how to create objects of type AuxBitplaneAttrib.
  */

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.